├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── CSharp ├── CancellationTokenExample.cs ├── Extensions.cs ├── ParallelExecutionExample.cs ├── README.MD └── SequentialExecutionExample.cs ├── JavaScript ├── 1-sequential-callbacks.js ├── 2-emulate-async-calls.js ├── 3-back-to-order.js ├── 4-step-by-step.js ├── 5-chain.js ├── 5a-chain.js ├── 5b-chain.js ├── 6-promise-sequential.js ├── 7-promise-all.js ├── 8-promise-mixed.js ├── 9-events.js ├── README.md ├── a-to-async.js ├── b-sync-to-promise.js ├── c-promisify.js ├── d-asyncjs-map.js ├── e-asyncjs-parallel.js ├── f-asyncjs-series.js ├── g-async-await.js ├── package-lock.json └── package.json ├── LICENSE └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [{*.js,*.mjs,*.ts,*.json,*.yml}] 11 | indent_size = 2 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "globals": { 12 | "BigInt": true 13 | }, 14 | "rules": { 15 | "indent": [ 16 | "error", 17 | 2 18 | ], 19 | "linebreak-style": [ 20 | "error", 21 | "unix" 22 | ], 23 | "quotes": [ 24 | "error", 25 | "single" 26 | ], 27 | "semi": [ 28 | "error", 29 | "always" 30 | ], 31 | "no-loop-func": [ 32 | "error" 33 | ], 34 | "block-spacing": [ 35 | "error", 36 | "always" 37 | ], 38 | "camelcase": [ 39 | "error" 40 | ], 41 | "eqeqeq": [ 42 | "error", 43 | "always" 44 | ], 45 | "strict": [ 46 | "error", 47 | "global" 48 | ], 49 | "brace-style": [ 50 | "error", 51 | "1tbs", 52 | { 53 | "allowSingleLine": true 54 | } 55 | ], 56 | "comma-style": [ 57 | "error", 58 | "last" 59 | ], 60 | "comma-spacing": [ 61 | "error", 62 | { 63 | "before": false, 64 | "after": true 65 | } 66 | ], 67 | "eol-last": [ 68 | "error" 69 | ], 70 | "func-call-spacing": [ 71 | "error", 72 | "never" 73 | ], 74 | "key-spacing": [ 75 | "error", 76 | { 77 | "beforeColon": false, 78 | "afterColon": true, 79 | "mode": "minimum" 80 | } 81 | ], 82 | "keyword-spacing": [ 83 | "error", 84 | { 85 | "before": true, 86 | "after": true, 87 | "overrides": { 88 | "function": { 89 | "after": false 90 | } 91 | } 92 | } 93 | ], 94 | "max-len": [ 95 | "error", 96 | { 97 | "code": 80, 98 | "ignoreUrls": true 99 | } 100 | ], 101 | "max-nested-callbacks": [ 102 | "error", 103 | { 104 | "max": 7 105 | } 106 | ], 107 | "new-cap": [ 108 | "error", 109 | { 110 | "newIsCap": true, 111 | "capIsNew": false, 112 | "properties": true 113 | } 114 | ], 115 | "new-parens": [ 116 | "error" 117 | ], 118 | "no-lonely-if": [ 119 | "error" 120 | ], 121 | "no-trailing-spaces": [ 122 | "error" 123 | ], 124 | "no-unneeded-ternary": [ 125 | "error" 126 | ], 127 | "no-whitespace-before-property": [ 128 | "error" 129 | ], 130 | "object-curly-spacing": [ 131 | "error", 132 | "always" 133 | ], 134 | "operator-assignment": [ 135 | "error", 136 | "always" 137 | ], 138 | "operator-linebreak": [ 139 | "error", 140 | "after" 141 | ], 142 | "semi-spacing": [ 143 | "error", 144 | { 145 | "before": false, 146 | "after": true 147 | } 148 | ], 149 | "space-before-blocks": [ 150 | "error", 151 | "always" 152 | ], 153 | "space-before-function-paren": [ 154 | "error", 155 | { 156 | "anonymous": "never", 157 | "named": "never", 158 | "asyncArrow": "always" 159 | } 160 | ], 161 | "space-in-parens": [ 162 | "error", 163 | "never" 164 | ], 165 | "space-infix-ops": [ 166 | "error" 167 | ], 168 | "space-unary-ops": [ 169 | "error", 170 | { 171 | "words": true, 172 | "nonwords": false, 173 | "overrides": { 174 | "typeof": false 175 | } 176 | } 177 | ], 178 | "no-unreachable": [ 179 | "error" 180 | ], 181 | "no-global-assign": [ 182 | "error" 183 | ], 184 | "no-self-compare": [ 185 | "error" 186 | ], 187 | "no-unmodified-loop-condition": [ 188 | "error" 189 | ], 190 | "no-constant-condition": [ 191 | "error", 192 | { 193 | "checkLoops": false 194 | } 195 | ], 196 | "no-console": [ 197 | "off" 198 | ], 199 | "no-useless-concat": [ 200 | "error" 201 | ], 202 | "no-useless-escape": [ 203 | "error" 204 | ], 205 | "no-shadow-restricted-names": [ 206 | "error" 207 | ], 208 | "no-use-before-define": [ 209 | "error", 210 | { 211 | "functions": false 212 | } 213 | ], 214 | "arrow-parens": [ 215 | "error", 216 | "always" 217 | ], 218 | "arrow-body-style": [ 219 | "error", 220 | "as-needed" 221 | ], 222 | "arrow-spacing": [ 223 | "error" 224 | ], 225 | "no-confusing-arrow": [ 226 | "error", 227 | { 228 | "allowParens": true 229 | } 230 | ], 231 | "no-useless-computed-key": [ 232 | "error" 233 | ], 234 | "no-useless-rename": [ 235 | "error" 236 | ], 237 | "no-var": [ 238 | "error" 239 | ], 240 | "object-shorthand": [ 241 | "error", 242 | "always" 243 | ], 244 | "prefer-arrow-callback": [ 245 | "error" 246 | ], 247 | "prefer-const": [ 248 | "error" 249 | ], 250 | "prefer-numeric-literals": [ 251 | "error" 252 | ], 253 | "prefer-rest-params": [ 254 | "error" 255 | ], 256 | "prefer-spread": [ 257 | "error" 258 | ], 259 | "rest-spread-spacing": [ 260 | "error", 261 | "never" 262 | ], 263 | "template-curly-spacing": [ 264 | "error", 265 | "never" 266 | ], 267 | "consistent-return": [ 268 | "error", 269 | { "treatUndefinedAsUnspecified": true } 270 | ] 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /CSharp/CancellationTokenExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace AsyncAndPromises 7 | { 8 | public class CancellationTokenExample 9 | { 10 | static readonly Random Random = new Random(); 11 | static readonly CancellationTokenSource cancelationSource = new CancellationTokenSource(); 12 | 13 | public static void Run() 14 | { 15 | Console.WriteLine("Cancelation Token example."); 16 | var stringBuilder = new StringBuilder(); 17 | 18 | var h = new Task(() => AddSymbol(stringBuilder, 'H'), cancelationSource.Token); 19 | var e = new Task(() => AddSymbol(stringBuilder, 'e'), cancelationSource.Token); 20 | var l = new Task(() => AddSymbol(stringBuilder, 'l'), cancelationSource.Token); 21 | var o = new Task(() => AddSymbol(stringBuilder, 'o'), cancelationSource.Token); 22 | 23 | Console.WriteLine("Press enter to stop execution"); 24 | 25 | h.Start(); 26 | var promise = h.Then(e) 27 | .Then(l) 28 | .Then(new Task(() => AddSymbol(stringBuilder, 'l'))) 29 | .Then(o); 30 | 31 | 32 | Console.ReadLine(); 33 | //Throw cancel request 34 | cancelationSource.Cancel(); 35 | 36 | try 37 | { 38 | //wait when all threads will be stopped 39 | promise.Wait(cancelationSource.Token); 40 | } 41 | catch (OperationCanceledException ex) 42 | { 43 | //it's expected exception 44 | } 45 | finally 46 | { 47 | Console.WriteLine("Result: {0}", stringBuilder); 48 | Console.ReadLine(); 49 | } 50 | } 51 | 52 | public static void AddSymbol(StringBuilder stringBuilder, 53 | char symbol) 54 | { 55 | //1-3 second 56 | Thread.Sleep(Random.Next(1000, 3000)); 57 | 58 | //Check if cancellation was requested 59 | cancelationSource.Token.ThrowIfCancellationRequested(); 60 | stringBuilder.Append(symbol); 61 | Console.WriteLine("Appended '{0}' symbol", symbol); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CSharp/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace AsyncAndPromises 8 | { 9 | /// 10 | /// Just add custom methods to Existing classes. 11 | /// It makes syntax like JavaScript 12 | /// 13 | public static class Extensions 14 | { 15 | public static Task Then(this Task task, Task nextTask) 16 | { 17 | task.ContinueWith(t => 18 | { 19 | if (!nextTask.IsCanceled) 20 | nextTask.Start(); 21 | }); 22 | 23 | return nextTask; 24 | } 25 | 26 | public static Task Then(this Task task, Action nextAction) 27 | { 28 | var nextTask = new Task(nextAction); 29 | return task.Then(nextTask); 30 | } 31 | 32 | public static Task Then(this Action action, Action nextAction) 33 | { 34 | var rootTask = new Task(action); 35 | rootTask.Start(); 36 | return rootTask.Then(nextAction); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CSharp/ParallelExecutionExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace AsyncAndPromises 7 | { 8 | public class ParallelExecutionExample 9 | { 10 | static readonly Random Random = new Random(); 11 | 12 | public static void SequentialPromiseExample() 13 | { 14 | var stringBuilder = new StringBuilder(); 15 | 16 | var h = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'H')); 17 | var e = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'e')); 18 | var l = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'l')); 19 | var o = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'o')); 20 | 21 | //Action will be executed parallely 22 | //Wait for all 23 | Task.WaitAll(h, e, l, o); 24 | 25 | Console.WriteLine(stringBuilder.ToString()); 26 | Console.ReadLine(); 27 | } 28 | 29 | public static void AddSymbol(StringBuilder stringBuilder, 30 | char symbol) 31 | { 32 | //1-3 seconds 33 | Thread.Sleep(Random.Next(1000, 3000)); 34 | 35 | stringBuilder.Append(symbol); 36 | Console.WriteLine("Appended '{0}' symbol", symbol); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CSharp/README.MD: -------------------------------------------------------------------------------- 1 |

C# Examples

2 | 3 | ## Parallel execution 4 | 5 | ```csharp 6 | var h = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'H')); 7 | var e = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'e')); 8 | var l = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'l')); 9 | var o = Task.Factory.StartNew(() => AddSymbol(stringBuilder, 'o')); 10 | 11 | //Action will be executed parallely 12 | //Wait for all 13 | Task.WaitAll(h, e, l, o); 14 | ``` 15 | Result: 16 | ![image](https://cloud.githubusercontent.com/assets/12159879/25973800/53eb500c-36ae-11e7-9ef3-43cb3c2b3331.png) 17 | 18 | ## Sequential execution 19 | 20 | ```csharp 21 | Action h = () => { AddSymbol(stringBuilder, 'H'); }; 22 | Action e = () => { AddSymbol(stringBuilder, 'e'); }; 23 | Action l = () => { AddSymbol(stringBuilder, 'l'); }; 24 | Action o = () => { AddSymbol(stringBuilder, 'o'); }; 25 | 26 | var promise = h.Then(e); 27 | promise = promise.Then(l) 28 | .Then(l) 29 | .Then(o); 30 | 31 | ``` 32 | Result: 33 | ![image](https://cloud.githubusercontent.com/assets/12159879/25973791/48de1438-36ae-11e7-85c1-ffc096d1eb4c.png) 34 | 35 | ## Cancellation Token 36 | 37 | ```csharp 38 | var h = new Task(() => AddSymbol(stringBuilder, 'H'), cancelationSource.Token); 39 | var e = new Task(() => AddSymbol(stringBuilder, 'e'), cancelationSource.Token); 40 | var l = new Task(() => AddSymbol(stringBuilder, 'l'), cancelationSource.Token); 41 | var o = new Task(() => AddSymbol(stringBuilder, 'o'), cancelationSource.Token); 42 | 43 | Console.WriteLine("Press enter to stop execution"); 44 | 45 | h.Start(); 46 | var promise = h.Then(e) 47 | .Then(l) 48 | .Then(new Task(() => AddSymbol(stringBuilder, 'l'))) 49 | .Then(o); 50 | 51 | 52 | Console.ReadLine(); 53 | //Throw cancel request 54 | cancelationSource.Cancel(); 55 | ``` 56 | Result: 57 | ![image](https://cloud.githubusercontent.com/assets/12159879/25973809/63b8e9cc-36ae-11e7-830b-01d985cfb4d6.png) 58 | -------------------------------------------------------------------------------- /CSharp/SequentialExecutionExample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace AsyncAndPromises 7 | { 8 | public class SequentialExecutionExample 9 | { 10 | static readonly Random Random = new Random(); 11 | 12 | public static void SequentialPromiseExample() 13 | { 14 | var stringBuilder = new StringBuilder(); 15 | 16 | var h = new Task(() => AddSymbol(stringBuilder, 'H')); 17 | var e = new Task(() => AddSymbol(stringBuilder, 'e')); 18 | var l = new Task(() => AddSymbol(stringBuilder, 'l')); 19 | var o = new Task(() => AddSymbol(stringBuilder, 'o')); 20 | 21 | //Functions will be executed in order as it was specified 22 | h.Start(); 23 | var promise = h.Then(e) 24 | .Then(l) 25 | .Then(new Task(() => AddSymbol(stringBuilder, 'l'))) 26 | .Then(o); 27 | 28 | promise.Wait(); 29 | 30 | Console.WriteLine(stringBuilder.ToString()); 31 | Console.ReadLine(); 32 | } 33 | 34 | public static void SequentialPromiseExampleImprovedSyntax() 35 | { 36 | var stringBuilder = new StringBuilder(); 37 | 38 | Action h = () => { AddSymbol(stringBuilder, 'H'); }; 39 | Action e = () => { AddSymbol(stringBuilder, 'e'); }; 40 | Action l = () => { AddSymbol(stringBuilder, 'l'); }; 41 | Action o = () => { AddSymbol(stringBuilder, 'o'); }; 42 | 43 | var promise = h.Then(e); 44 | promise = promise.Then(l) 45 | .Then(l) 46 | .Then(o); 47 | 48 | promise.Wait(); 49 | 50 | Console.WriteLine(stringBuilder.ToString()); 51 | Console.ReadLine(); 52 | } 53 | 54 | public static void AddSymbol(StringBuilder stringBuilder, 55 | char symbol) 56 | { 57 | //1-3 seconds 58 | Thread.Sleep(Random.Next(1000, 3000)); 59 | 60 | stringBuilder.Append(symbol); 61 | Console.WriteLine("Appended '{0}' symbol", symbol); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /JavaScript/1-sequential-callbacks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Sequential calls and sequentian execution 4 | // of 4 pseudo-asynchronous functions 5 | 6 | const readConfig = (name, callback) => { 7 | setTimeout(() => { 8 | console.log('(1) config loaded: ' + name); 9 | callback(null, { name }); 10 | }, 1000); 11 | }; 12 | 13 | const doQuery = (statement, callback) => { 14 | setTimeout(() => { 15 | console.log('(2) SQL query executed: ' + statement); 16 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' }]); 17 | }, 1000); 18 | }; 19 | 20 | const httpGet = (url, callback) => { 21 | setTimeout(() => { 22 | console.log('(3) Page retrieved: ' + url); 23 | callback(null, 'Some archaic web here'); 24 | }, 1000); 25 | }; 26 | 27 | const readFile = (path, callback) => { 28 | setTimeout(() => { 29 | console.log('(4) Readme file loaded'); 30 | callback(null, 'file content'); 31 | }, 1000); 32 | }; 33 | 34 | console.log('start'); 35 | 36 | readConfig('myConfig', () => {}); 37 | doQuery('select * from cities', () => {}); 38 | httpGet('http://kpi.ua', () => {}); 39 | readFile('README.md', () => {}); 40 | 41 | console.log('end'); 42 | -------------------------------------------------------------------------------- /JavaScript/2-emulate-async-calls.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Run and see random order 4 | // Use global counter to detect finish (bad practice) 5 | 6 | const callbackCounter = (count, callback) => { 7 | let counter = 0; 8 | const done = () => { 9 | if (++counter === count) callback(); 10 | }; 11 | return done; 12 | }; 13 | 14 | // Emulate asynchronous calls 15 | 16 | const wrapAsync = (fn) => (...args) => setTimeout( 17 | () => fn(...args), Math.floor(Math.random() * 1000) 18 | ); 19 | 20 | // Asynchronous functions 21 | 22 | const readConfig = wrapAsync((name, callback) => { 23 | console.log('(1) config loaded'); 24 | callback(null, { name }); 25 | }); 26 | 27 | const doQuery = wrapAsync((statement, callback) => { 28 | console.log('(2) SQL query executed: ' + statement); 29 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' }]); 30 | }); 31 | 32 | const httpGet = wrapAsync((url, callback) => { 33 | console.log('(3) Page retrieved: ' + url); 34 | callback(null, 'Some archaic web here'); 35 | }); 36 | 37 | const readFile = wrapAsync((path, callback) => { 38 | console.log('(4) Readme file loaded'); 39 | callback(null, 'file content'); 40 | }); 41 | 42 | console.log('start'); 43 | 44 | const callback = callbackCounter(4, () => { 45 | console.log('All done!'); 46 | }); 47 | 48 | readConfig('myConfig', callback); 49 | doQuery('select * from cities', callback); 50 | httpGet('http://kpi.ua', callback); 51 | readFile('README.md', callback); 52 | 53 | console.log('end'); 54 | -------------------------------------------------------------------------------- /JavaScript/3-back-to-order.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Back to order, enclosed callbacks 4 | 5 | // Emulate asynchronous calls 6 | 7 | const wrapAsync = (fn) => (...args) => setTimeout( 8 | () => fn(...args), Math.floor(Math.random() * 1000) 9 | ); 10 | 11 | // Asynchronous functions 12 | 13 | const readConfig = wrapAsync((name, callback) => { 14 | console.log('(1) config loaded'); 15 | callback(null, { name }); 16 | }); 17 | 18 | const selectFromDb = wrapAsync((query, callback) => { 19 | console.log('(2) SQL query executed'); 20 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' }]); 21 | }); 22 | 23 | const getHttpPage = wrapAsync((url, callback) => { 24 | console.log('(3) Page retrieved'); 25 | callback(null, 'Some archaic web here'); 26 | }); 27 | 28 | const readFile = wrapAsync((path, callback) => { 29 | console.log('(4) Readme file loaded'); 30 | callback(null, 'file content'); 31 | }); 32 | 33 | readConfig('myConfig', () => { 34 | selectFromDb('select * from cities', () => { 35 | getHttpPage('http://kpi.ua', () => { 36 | readFile('README.md', () => { 37 | console.log('All done!'); 38 | }); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /JavaScript/4-step-by-step.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Back to order 4 | // Use global data and decentralized control flow (bad practices) 5 | 6 | const data = {}; 7 | 8 | // Emulate asynchronous calls 9 | 10 | const wrapAsync = (fn) => (...args) => setTimeout( 11 | () => fn(...args), Math.floor(Math.random() * 1000) 12 | ); 13 | 14 | // Asynchronous functions 15 | 16 | const readFile = wrapAsync(() => { 17 | console.log('(4) Readme file loaded'); 18 | data.readme = 'file content'; 19 | console.dir(data); 20 | console.log('All done!'); 21 | }); 22 | 23 | const getHttpPage = wrapAsync(() => { 24 | console.log('(3) Page retrieved'); 25 | data.html = 'Some archaic web here'; 26 | readFile(); 27 | }); 28 | 29 | const selectFromDb = wrapAsync(() => { 30 | console.log('(2) SQL query executed'); 31 | data.cities = [{ name: 'Kiev' }, { name: 'Roma' }]; 32 | getHttpPage(); 33 | }); 34 | 35 | const readConfig = wrapAsync(() => { 36 | console.log('(1) config loaded'); 37 | data.config = { name: 'name' }; 38 | selectFromDb(); 39 | }); 40 | 41 | // Start execution 42 | 43 | readConfig(); 44 | -------------------------------------------------------------------------------- /JavaScript/5-chain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use list and chaining syntax to build sequence 4 | 5 | const chain = (prev = null) => { 6 | console.log('Create element'); 7 | const cur = () => { 8 | console.log('Reverse from ' + (cur.fn ? cur.fn.name : 'null')); 9 | if (cur.prev) { 10 | cur.prev.next = cur; 11 | cur.prev(); 12 | } else { 13 | cur.forward(); 14 | } 15 | }; 16 | cur.prev = prev; 17 | cur.fn = null; 18 | cur.args = null; 19 | cur.do = (fn, ...args) => { 20 | cur.fn = fn; 21 | cur.args = args; 22 | return chain(cur); 23 | }; 24 | cur.forward = () => { 25 | console.log('Forward'); 26 | if (cur.fn) cur.fn(...cur.args, (err, data) => { 27 | console.log('Callback from ' + cur.fn.name); 28 | console.dir({ data }); 29 | if (!err && cur.next) cur.next.forward(); 30 | else console.log('End at ' + cur.fn.name); 31 | }); 32 | }; 33 | return cur; 34 | }; 35 | 36 | // Emulate asynchronous calls 37 | 38 | const wrapAsync = (fn) => (...args) => setTimeout( 39 | () => fn(...args), Math.floor(Math.random() * 1000) 40 | ); 41 | 42 | // Asynchronous functions 43 | 44 | const readConfig = wrapAsync((name, callback) => { 45 | console.log('(1) config loaded'); 46 | callback(null, { name }); 47 | }); 48 | 49 | const selectFromDb = wrapAsync((query, callback) => { 50 | console.log('(2) SQL query executed'); 51 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' } ]); 52 | }); 53 | 54 | const getHttpPage = wrapAsync((url, callback) => { 55 | console.log('(3) Page retrieved'); 56 | callback(null, 'Some archaic web here'); 57 | }); 58 | 59 | const readFile = wrapAsync((path, callback) => { 60 | console.log('(4) Readme file loaded'); 61 | callback(null, 'file content'); 62 | }); 63 | 64 | // Usage 65 | 66 | const startChain = chain() 67 | .do(readConfig, 'myConfig') 68 | .do(selectFromDb, 'select * from cities') 69 | .do(getHttpPage, 'http://kpi.ua') 70 | .do(readFile, 'README.md'); 71 | 72 | startChain(); 73 | -------------------------------------------------------------------------------- /JavaScript/5a-chain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use list and chaining syntax to build sequence 4 | 5 | const chain = () => { 6 | const callbacks = []; 7 | 8 | const nextCallback = () => callbacks.shift(); 9 | 10 | const forward = (err, data) => { 11 | if (err) return console.log({ err }); 12 | if (data) console.log('Next callback will get:', { data }); 13 | 14 | const next = nextCallback(); 15 | if (!next) return; 16 | 17 | const [callback, name] = next; 18 | console.log('Calling:', name); 19 | // Pass data from previous callback 20 | callback(data, forward); 21 | }; 22 | 23 | forward.do = (func, name) => { 24 | callbacks.push([func, name]); 25 | return forward; 26 | }; 27 | 28 | return forward; 29 | }; 30 | 31 | // Emulate asynchronous calls 32 | 33 | const wrapAsync = (fn) => (...args) => setTimeout( 34 | () => fn(...args), Math.floor(Math.random() * 1000) 35 | ); 36 | 37 | // Asynchronous functions 38 | 39 | const readConfig = wrapAsync((name, callback) => { 40 | console.log('(1) config loaded'); 41 | callback(null, { name }); 42 | }); 43 | 44 | const selectFromDb = wrapAsync((query, callback) => { 45 | console.log('(2) SQL query executed'); 46 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' } ]); 47 | }); 48 | 49 | const getHttpPage = wrapAsync((url, callback) => { 50 | console.log('(3) Page retrieved'); 51 | callback(null, 'Some archaic web here'); 52 | }); 53 | 54 | const readFile = wrapAsync((path, callback) => { 55 | console.log('(4) Readme file loaded'); 56 | callback(null, 'file content'); 57 | }); 58 | 59 | // Usage 60 | 61 | const startChain = chain() 62 | .do(readConfig, 'myConfig') 63 | .do(selectFromDb, 'select * from cities') 64 | .do(getHttpPage, 'http://kpi.ua') 65 | .do(readFile, 'README.md'); 66 | 67 | startChain(); 68 | -------------------------------------------------------------------------------- /JavaScript/5b-chain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use list and chaining syntax to build sequence 4 | 5 | const chain = () => { 6 | const callbacks = []; 7 | 8 | const nextCallback = () => callbacks.shift(); 9 | 10 | const forward = (err, data) => { 11 | if (err) return console.log({ err }); 12 | if (data) console.log('Callback data:', { data }); 13 | 14 | const next = nextCallback(); 15 | if (!next) return; 16 | 17 | const [callback, args] = next; 18 | console.log('Calling next with args:', { args }); 19 | // Pass second argument from .do 20 | callback(args, forward); 21 | }; 22 | 23 | forward.do = (func, name) => { 24 | callbacks.push([func, name]); 25 | return forward; 26 | }; 27 | 28 | return forward; 29 | }; 30 | 31 | // Emulate asynchronous calls 32 | 33 | const wrapAsync = (fn) => (...args) => setTimeout( 34 | () => fn(...args), Math.floor(Math.random() * 1000) 35 | ); 36 | 37 | // Asynchronous functions 38 | 39 | const readConfig = wrapAsync((name, callback) => { 40 | console.log('(1) config loaded'); 41 | callback(null, { name }); 42 | }); 43 | 44 | const selectFromDb = wrapAsync((query, callback) => { 45 | console.log('(2) SQL query executed'); 46 | callback(null, [{ name: 'Kiev' }, { name: 'Roma' } ]); 47 | }); 48 | 49 | const getHttpPage = wrapAsync((url, callback) => { 50 | console.log('(3) Page retrieved'); 51 | callback(null, 'Some archaic web here'); 52 | }); 53 | 54 | const readFile = wrapAsync((path, callback) => { 55 | console.log('(4) Readme file loaded'); 56 | callback(null, 'file content'); 57 | }); 58 | 59 | // Usage 60 | 61 | const startChain = chain() 62 | .do(readConfig, 'myConfig') 63 | .do(selectFromDb, 'select * from cities') 64 | .do(getHttpPage, 'http://kpi.ua') 65 | .do(readFile, 'README.md'); 66 | 67 | startChain(); 68 | -------------------------------------------------------------------------------- /JavaScript/6-promise-sequential.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Emulate asynchronous calls 4 | 5 | const wrapAsync = (callback) => setTimeout( 6 | callback, Math.floor(Math.random() * 1000) 7 | ); 8 | 9 | const isWeekend = () => !(new Date().getDay() % 6); 10 | 11 | // Asynchronous functions 12 | 13 | const readConfig = (name) => new Promise((resolve, reject) => { 14 | wrapAsync(() => { 15 | console.log('(1) config loaded'); 16 | if (!isWeekend()) resolve({ name }); 17 | else reject(new Error('Promises will resolve next working day')); 18 | }); 19 | }); 20 | 21 | const doQuery = (statement) => new Promise((resolve, reject) => { 22 | wrapAsync(() => { 23 | console.log('(2) SQL query executed: ' + statement); 24 | if (!isWeekend()) resolve([{ name: 'Kiev' }, { name: 'Roma' }]); 25 | else reject(new Error('Promises will resolve next working day')); 26 | }); 27 | }); 28 | 29 | const httpGet = (url) => new Promise((resolve, reject) => { 30 | wrapAsync(() => { 31 | console.log('(3) Page retrieved: ' + url); 32 | if (!isWeekend()) resolve('Some archaic web here'); 33 | else reject(new Error('Promises will resolve next working day')); 34 | }); 35 | }); 36 | 37 | const readFile = (path) => new Promise((resolve, reject) => { 38 | wrapAsync(() => { 39 | console.log('(4) Readme file loaded: ' + path); 40 | if (!isWeekend()) resolve('file content'); 41 | else reject(new Error('Promises will resolve next working day')); 42 | }); 43 | }); 44 | 45 | // Usage 46 | 47 | Promise.resolve() 48 | .then(readConfig.bind(null, 'myConfig')) 49 | .then(doQuery.bind(null, 'select * from cities')) 50 | .then(httpGet.bind(null, 'http://kpi.ua')) 51 | .catch((err) => console.log('Reject reason (1): ' + err.message)) 52 | .then(readFile.bind(null, 'README.md')) 53 | .catch((err) => console.log('Reject reason (2): ' + err.message)) 54 | .then((data) => { 55 | console.log('Done'); 56 | console.dir({ data }); 57 | }); 58 | -------------------------------------------------------------------------------- /JavaScript/7-promise-all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Emulate asynchronous calls 4 | 5 | const wrapAsync = (callback) => setTimeout( 6 | callback, Math.floor(Math.random() * 1000) 7 | ); 8 | 9 | // Asynchronous functions 10 | 11 | const readConfig = (name) => new Promise((resolve) => { 12 | wrapAsync(() => { 13 | console.log('(1) config loaded: ' + name); 14 | resolve({ name }); 15 | }); 16 | }); 17 | 18 | const doQuery = (statement) => new Promise((resolve) => { 19 | wrapAsync(() => { 20 | console.log('(2) SQL query executed: ' + statement); 21 | resolve([ { name: 'Kiev' }, { name: 'Roma' } ]); 22 | }); 23 | }); 24 | 25 | const httpGet = (url) => new Promise((resolve) => { 26 | wrapAsync(() => { 27 | console.log('(3) Page retrieved: ' + url); 28 | resolve('Some archaic web here'); 29 | }); 30 | }); 31 | 32 | const readFile = (path) => new Promise((resolve) => { 33 | wrapAsync(() => { 34 | console.log('(4) Readme file loaded: ' + path); 35 | resolve('file content'); 36 | }); 37 | }); 38 | 39 | // Usage 40 | 41 | Promise.all([ 42 | readConfig('myConfig'), 43 | doQuery('select * from cities'), 44 | httpGet('http://kpi.ua'), 45 | readFile('README.md') 46 | ]).then((data) => { 47 | console.log('Done'); 48 | console.dir({ data }); 49 | }); 50 | -------------------------------------------------------------------------------- /JavaScript/8-promise-mixed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Emulate asynchronous calls 4 | 5 | const wrapAsync = (callback) => setTimeout( 6 | callback, Math.floor(Math.random() * 1000) 7 | ); 8 | 9 | // Asynchronous functions 10 | 11 | const readConfig = (name) => new Promise((resolve) => { 12 | wrapAsync(() => { 13 | console.log('(1) config loaded: ' + name); 14 | resolve({ name }); 15 | }); 16 | }); 17 | 18 | const doQuery = (statement) => new Promise((resolve) => { 19 | wrapAsync(() => { 20 | console.log('(2) SQL query executed: ' + statement); 21 | resolve([ { name: 'Kiev' }, { name: 'Roma' } ]); 22 | }); 23 | }); 24 | 25 | const httpGet = (url) => new Promise((resolve) => { 26 | wrapAsync(() => { 27 | console.log('(3) Page retrieved: ' + url); 28 | resolve('Some archaic web here'); 29 | }); 30 | }); 31 | 32 | const readFile = (path) => new Promise((resolve) => { 33 | wrapAsync(() => { 34 | console.log('(4) Readme file loaded: ' + path); 35 | resolve('file content'); 36 | }); 37 | }); 38 | 39 | // Usage 40 | 41 | Promise.resolve() 42 | .then(readConfig.bind(null, 'myConfig')) 43 | .then(() => Promise.all([ 44 | doQuery('select * from cities'), 45 | httpGet('http://kpi.ua') 46 | ])) 47 | .then(readFile.bind(null, 'README.md')) 48 | .then((data) => { 49 | console.log('Done'); 50 | console.dir({ data }); 51 | }); 52 | -------------------------------------------------------------------------------- /JavaScript/9-events.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { EventEmitter } = require('node:events'); 4 | const ee = new EventEmitter(); 5 | 6 | // Emulate asynchronous calls 7 | 8 | const wrapAsync = (fn) => (...args) => setTimeout( 9 | () => fn(...args), Math.floor(Math.random() * 1000) 10 | ); 11 | 12 | // Asynchronous functions 13 | 14 | const readConfig = wrapAsync((name) => { 15 | console.log('(1) config loaded'); 16 | ee.emit('config', { name }); 17 | }); 18 | 19 | const doQuery = wrapAsync((statement) => { 20 | console.log('(2) SQL query executed: ' + statement); 21 | ee.emit('query', [ { name: 'Kiev' }, { name: 'Roma' } ]); 22 | }); 23 | 24 | const httpGet = wrapAsync((url) => { 25 | console.log('(3) Page retrieved: ' + url); 26 | ee.emit('page', 'Some archaic web here'); 27 | }); 28 | 29 | const readFile = wrapAsync((path) => { 30 | console.log('(4) Readme file loaded: ' + path); 31 | ee.emit('done', 'file content'); 32 | }); 33 | 34 | // Example 35 | 36 | console.log('start'); 37 | 38 | readConfig('myConfig'); 39 | ee.on('config', doQuery.bind(null, 'select * from cities')); 40 | ee.on('query', httpGet.bind(null, 'http://kpi.ua')); 41 | ee.on('page', readFile.bind(null, 'README.md')); 42 | ee.on('done', () => console.log('done')); 43 | 44 | console.log('end'); 45 | -------------------------------------------------------------------------------- /JavaScript/README.md: -------------------------------------------------------------------------------- 1 | # AsynchronousProgramming 2 | 3 | See four examples and execute them. Create asynchronous analogues of `Array.forEach` 4 | 5 | ## Parallel execution 6 | 7 | ```js 8 | // Array of functions 9 | let fns = [readConfig, selectFromDb, getHttpPage, readFile]; 10 | 11 | // Call example 12 | parallelAsync(fns, () => { console.log('Done'); }); 13 | 14 | // Implementation 15 | function parallelAsync(fns, done) { 16 | // TODO: implement parallel ecexution 17 | }; 18 | ``` 19 | 20 | ## Sequential execution 21 | 22 | ```js 23 | // Array of functions 24 | let fns = [readConfig, selectFromDb, getHttpPage, readFile]; 25 | 26 | // Call example 27 | sequentialAsync(fns, () => { console.log('Done'); }); 28 | 29 | // Implementation 30 | function sequentialAsync(fns, done) { 31 | // TODO: implement sequential execution 32 | }; 33 | ``` 34 | -------------------------------------------------------------------------------- /JavaScript/a-to-async.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Sync function to async 4 | 5 | const last = (arr) => arr[arr.length - 1]; 6 | 7 | const asyncify = (fn) => (...args) => { 8 | const callback = last(args); 9 | args.pop(); 10 | process.nextTick(() => { 11 | callback(null, fn(...args)); 12 | }); 13 | }; 14 | 15 | // Functions 16 | 17 | const f1 = (par) => par; 18 | const f2 = (par) => par; 19 | const f3 = (par) => par; 20 | const f4 = (par) => par; 21 | 22 | // Usage 23 | 24 | const af1 = asyncify(f1); 25 | const af2 = asyncify(f2); 26 | const af3 = asyncify(f3); 27 | const af4 = asyncify(f4); 28 | 29 | af1('value2', (e, data) => { 30 | af2(data, (e, data) => { 31 | af3(data, (e, data) => { 32 | af4(data, (e, data) => { 33 | console.dir({ data }); 34 | }); 35 | }); 36 | }); 37 | }); 38 | 39 | console.log(f4(f3(f2(f1('value'))))); 40 | -------------------------------------------------------------------------------- /JavaScript/b-sync-to-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Sync function to Promise 4 | 5 | const promisifySync = (fn) => (...args) => ( 6 | new Promise((resolve, reject) => { 7 | const result = fn(...args); 8 | if (result instanceof Error) reject(result); 9 | else resolve(result); 10 | }) 11 | ); 12 | 13 | // Usage 14 | 15 | const f1 = (par) => par; 16 | 17 | console.log('Sync execution:', f1('value')); 18 | 19 | const pf1 = promisifySync(f1); 20 | 21 | pf1('value') 22 | .then((data) => console.log('Promise execution:', data)) 23 | .catch((err) => console.error(err)); 24 | -------------------------------------------------------------------------------- /JavaScript/c-promisify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Async function to Promise 4 | 5 | const promisify = (fn) => (...args) => ( 6 | new Promise((resolve, reject) => ( 7 | fn(...args, (err, data) => ( 8 | err ? reject(err) : resolve(data) 9 | )) 10 | )) 11 | ); 12 | 13 | // Usage 14 | 15 | const f1 = (par, cb) => cb(null, par); 16 | 17 | f1('value', (err, par) => ( 18 | console.log('Async execution:', par) 19 | )); 20 | 21 | const pf1 = promisify(f1); 22 | 23 | pf1('value') 24 | .then((data) => console.log('Promise execution:', data)) 25 | .catch((err) => console.error(err)); 26 | -------------------------------------------------------------------------------- /JavaScript/d-asyncjs-map.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const async = require('async'); 4 | 5 | console.log('Map array'); 6 | 7 | const arr = [1, 2, 3, 4]; 8 | 9 | async.map( 10 | arr, 11 | (item, callback) => { 12 | console.dir({ item }); 13 | callback(null, item * 2); 14 | }, 15 | (err, res) => { 16 | console.dir({ err, res }); 17 | } 18 | ); 19 | 20 | console.log('Map object'); 21 | 22 | const obj = { a: 1, b: 2, c: 3, d: 4 }; 23 | 24 | async.map( 25 | obj, 26 | (item, callback) => { 27 | console.dir({ item }); 28 | if (item === 2) callback(new Error('Oh, shit'), 'value'); 29 | else callback(null, item * 2); 30 | }, 31 | (err, res) => { 32 | console.dir({ error: err.message, res }); 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /JavaScript/e-asyncjs-parallel.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const async = require('async'); 4 | 5 | console.log('Parallel array of function'); 6 | 7 | const arr = [ 8 | (callback) => callback(null, 'uno'), 9 | (callback) => callback(null, 'due'), 10 | (callback) => callback(null, 'tre') 11 | ]; 12 | 13 | async.parallel( 14 | arr, 15 | (err, res) => { 16 | console.dir({ err, res }); 17 | } 18 | ); 19 | 20 | console.log('Parallel hash of function'); 21 | 22 | const obj = { 23 | key1: (callback) => callback(null, 'uno'), 24 | key2: (callback) => callback(new Error('Oh, shit'), 'due'), 25 | key3: (callback) => callback(null, 'tre') 26 | }; 27 | 28 | async.parallel( 29 | obj, 30 | (err, res) => { 31 | console.dir({ error: err.message, res }); 32 | } 33 | ); 34 | -------------------------------------------------------------------------------- /JavaScript/f-asyncjs-series.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const async = require('async'); 4 | 5 | console.log('Parallel array of function'); 6 | 7 | const arr = [ 8 | (callback) => callback(null, 'uno'), 9 | (callback) => callback(null, 'due'), 10 | (callback) => callback(null, 'tre') 11 | ]; 12 | 13 | async.series( 14 | arr, 15 | (err, res) => { 16 | console.dir({ err, res }); 17 | } 18 | ); 19 | 20 | console.log('Parallel hash of function'); 21 | 22 | const obj = { 23 | key1: (callback) => callback(null, 'uno'), 24 | key2: (callback) => callback(new Error('Oh, shit'), 'due'), 25 | key3: (callback) => callback(null, 'tre') 26 | }; 27 | 28 | async.series( 29 | obj, 30 | (err, res) => { 31 | console.dir({ error: err.message, res }); 32 | } 33 | ); 34 | -------------------------------------------------------------------------------- /JavaScript/g-async-await.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Emulate Asynchronous calls 4 | 5 | const pause = () => new Promise((resolve) => 6 | setTimeout(resolve, Math.floor(Math.random() * 1000)) 7 | ); 8 | 9 | // Asynchronous functions 10 | 11 | const readConfig = async (name) => { 12 | await pause(); 13 | console.log('(1) config loaded'); 14 | return { name }; 15 | }; 16 | 17 | const doQuery = async (statement) => { 18 | await pause(); 19 | console.log('(2) SQL query executed: ' + statement); 20 | return [{ name: 'Kiev' }, { name: 'Roma' }]; 21 | }; 22 | 23 | const httpGet = async (url) => { 24 | await pause(); 25 | console.log('(3) Page retrieved: ' + url); 26 | return 'Some archaic web here'; 27 | }; 28 | 29 | const readFile = async (path) => { 30 | await pause(); 31 | console.log('(4) Readme file loaded: ' + path); 32 | return 'file content'; 33 | }; 34 | 35 | // Usage 36 | 37 | (async () => { 38 | const config = await readConfig('myConfig'); 39 | const res = await doQuery('select * from cities'); 40 | let json, file; 41 | try { 42 | json = await httpGet('http://kpi.ua'); 43 | } catch (err) { 44 | console.log('Error (1): ' + err.message); 45 | } 46 | try { 47 | file = await readFile('README.md'); 48 | } catch (err) { 49 | console.log('Reject reason (2): ' + err.message); 50 | } 51 | console.log('Done'); 52 | console.dir({ config, res, json, file }); 53 | })(); 54 | -------------------------------------------------------------------------------- /JavaScript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "example", 8 | "license": "MIT", 9 | "dependencies": { 10 | "async": "^3.2.4" 11 | } 12 | }, 13 | "node_modules/async": { 14 | "version": "3.2.4", 15 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 16 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" 17 | } 18 | }, 19 | "dependencies": { 20 | "async": { 21 | "version": "3.2.4", 22 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 23 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /JavaScript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "author": "Timur Shemsedinov ", 4 | "license": "MIT", 5 | "dependencies": { 6 | "async": "^3.2.4" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2023 How.Programming.Works contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asynchronous Programming 2 | 3 | [👉 Оглавление курса Async 2024](https://github.com/HowProgrammingWorks/Index/blob/master/Courses/Async-2024.md) 4 | 5 | [![Асинхронное программирование на callback'ах](https://img.youtube.com/vi/z8Hg6zgi3yQ/0.jpg)](https://www.youtube.com/watch?v=z8Hg6zgi3yQ) 6 | [![Асинхронность с библиотекой async.js](https://img.youtube.com/vi/XQ94wQc-erU/0.jpg)](https://www.youtube.com/watch?v=XQ94wQc-erU) 7 | 8 | - callback 9 | - global or context counters, collections and flags 10 | - utilities (async.js and analogues) 11 | - data flow (collection iteration utilities) `f(collection, callback, done)` 12 | - control flow: `parallel/sequence(funcs, done)` 13 | - Chain/do 14 | - EventEmitter 15 | - Collector 16 | - Promises 17 | - async/await 18 | - Observable 19 | - generators/yield 20 | --------------------------------------------------------------------------------