├── .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 | 
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 | 
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 | 
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 | [](https://www.youtube.com/watch?v=z8Hg6zgi3yQ)
6 | [](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 |
--------------------------------------------------------------------------------