From 71dd4ba2268a43bff5c9d8e419bf6aaade47b8fb Mon Sep 17 00:00:00 2001 From: Dumitru Deveatii Date: Tue, 29 Jan 2019 17:25:00 +0200 Subject: [PATCH] Concurrency --- README.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 88a1ecb..bc3fb07 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Inspired from [clean-code-javascript](https://github.com/ryanmcdermott/clean-cod 5. [Classes](#classes) *TODO* 6. [SOLID](#solid) *TODO* 7. [Testing](#testing) *TODO* - 8. [Concurrency](#concurrency) *TODO* + 8. [Concurrency](#concurrency) 9. [Error Handling](#error-handling) *TODO* 10. [Formatting](#formatting) *TODO* 11. [Comments](#comments) @@ -365,6 +365,122 @@ interface Config { } ``` +## Concurrency + +### Prefer promises vs callbacks + +Callbacks aren't clean, and they cause excessive amounts of nesting *(the callback hell)*. +There are utilities that transform existing functions using the callback style to a version that returns promises +(for Node.js see [`util.promisify`](https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original), for general purpose see [pify](https://www.npmjs.com/package/pify), [es6-promisify](https://www.npmjs.com/package/es6-promisify)) + +**Bad:** + +```ts +import { get } from 'request'; +import { writeFile } from 'fs'; + +function downloadPage(url: string, saveTo: string, callback: (error: Error, content?: string) => void){ + get(url, (error, response) => { + if (error) { + callback(error); + } else { + writeFile(saveTo, response.body, (error) => { + if (error) { + callback(error); + } else { + callback(null, response.body); + } + }); + } + }) +} + +downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html', (error, content) => { + if (error) { + console.error(error); + } else { + console.log(content); + } +}); +``` + +Promises supports a few patterns that could be useful in some cases: +|Pattern|Description| +|-------|-----------| +|`Promise.resolve(value)`|Convert a value into a resolved promise.| +|`Promise.reject(error)`|Convert an error into a rejected promise.| +|`Promise.all(promises)`|Returns a new promise which is fulfilled with an array of fulfillment values for the passed promises or rejects with the reason of the first promise that rejects.| +|`Promise.race(promises)`|Returns a new promise which is fulfilled/rejected with the result/error of the first settled promise from the array of passed promises.| + +`Promise.all` is especially useful when there is a need to run tasks in parallel. `Promise.race` makes it easier to implement things like timeouts for promises. + +**Good:** + +```ts +import { get } from 'request'; +import { writeFile } from 'fs'; +import { promisify } from 'util'; + +const write = promisify(writeFile); + +function downloadPage(url: string, saveTo: string): Promise { + return get(url) + .then(response => write(saveTo, response)) +} + +downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html') + .then(content => console.log(content)) + .catch(error => console.error(error)); +``` + +**[⬆ back to top](#table-of-contents)** + +### Async/Await are even cleaner than Promises + +With async/await syntax you can write code that is far cleaner and more understandable that chained promises. Within a function prefixed with `async` keyword you have a way to tell the JavaScript runtime to pause the execution of code on the `await` keyword (when used on a promise). + +**Bad:** + +```ts +import { get } from 'request'; +import { writeFile } from 'fs'; +import { promisify } from 'util'; + +const write = util.promisify(writeFile); + +function downloadPage(url: string, saveTo: string): Promise { + return get(url).then(response => write(saveTo, response)) +} + +downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html') + .then(content => console.log(content)) + .catch(error => console.error(error)); +``` + +**Good:** + +```ts +import { get } from 'request'; +import { writeFile } from 'fs'; +import { promisify } from 'util'; + +const write = promisify(writeFile); + +async function downloadPage(url: string, saveTo: string): Promise { + const response = await get(url); + await write(saveTo, response); + return response; +} + +// somewhere in an async function +try { + const content = await downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html'); + console.log(content); +} catch (error) { + console.error(error); +} +``` + ## Comments The use of a comments is an indication of failure to express without them. Code should be the only source of truth.