├── JavaScript ├── file1.txt ├── file2.txt ├── file3.txt ├── 7-http.js ├── a-any.js ├── 9-race.js ├── 5-finally.js ├── 8-all.js ├── 0-contract.js ├── 1-callbacks.js ├── 6-fetch.js ├── Tasks │ ├── 2-promise.js │ ├── 3-chain.js │ ├── 4-iterate.js │ ├── 1-adapter.js │ └── 5-reject.js ├── 4-catch.js ├── server.js ├── b-thenable.js ├── d-resolvers.js ├── 3-promisify.js ├── e-try.js ├── 2-promise.js └── c-expirable.js ├── .gitignore ├── eslint.config.js ├── prettier.config.js ├── .editorconfig ├── README.md ├── package.json ├── LICENSE └── .eslintrc.json /JavaScript/file1.txt: -------------------------------------------------------------------------------- 1 | Text in the first file 2 | -------------------------------------------------------------------------------- /JavaScript/file2.txt: -------------------------------------------------------------------------------- 1 | Text in the second file 2 | -------------------------------------------------------------------------------- /JavaScript/file3.txt: -------------------------------------------------------------------------------- 1 | Text in the third file 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const init = require('eslint-config-metarhia'); 4 | 5 | init[0].rules['no-unused-vars'] = 'off'; 6 | 7 | module.exports = init; 8 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | printWidth: 80, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | tabWidth: 2, 8 | useTabs: false, 9 | semi: true, 10 | }; 11 | -------------------------------------------------------------------------------- /JavaScript/7-http.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fetch = require('./6-fetch.js'); 4 | 5 | fetch('http://localhost:3000/person') 6 | .then((data) => { 7 | console.log(data); 8 | }) 9 | .catch((err) => { 10 | console.error(err); 11 | }); 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Asynchronity with Promise 2 | 3 | [👉 Оглавление курса Async 2024](https://github.com/HowProgrammingWorks/Index/blob/master/Courses/Async-2024.md) 4 | 5 | [![Асинхронность на промисах, Promise, all, then, catch, race](https://img.youtube.com/vi/RMl4r6s1Y8M/0.jpg)](https://www.youtube.com/watch?v=RMl4r6s1Y8M) 6 | -------------------------------------------------------------------------------- /JavaScript/a-any.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const baseUrl = 'http://localhost:3000'; 4 | 5 | const promises = [ 6 | fetch(baseUrl + '/person'), 7 | fetch(baseUrl + '/'), 8 | fetch(baseUrl + '/city'), 9 | ]; 10 | 11 | Promise.any(promises) 12 | .then((res) => { 13 | console.log(res); 14 | }) 15 | .catch((err) => { 16 | console.log(err); 17 | }); 18 | -------------------------------------------------------------------------------- /JavaScript/9-race.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const baseUrl = 'http://localhost:3000'; 4 | 5 | const promises = [ 6 | fetch(baseUrl + '/person'), 7 | fetch(baseUrl + '/'), 8 | fetch(baseUrl + '/city'), 9 | ]; 10 | 11 | Promise.race(promises) 12 | .then((res) => { 13 | console.log(res); 14 | }) 15 | .catch((err) => { 16 | console.log(err); 17 | }); 18 | -------------------------------------------------------------------------------- /JavaScript/5-finally.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { readFile } = require('node:fs/promises'); 4 | 5 | readFile('file1.txt', 'utf8') 6 | .then((data) => { 7 | console.dir({ file1: data }); 8 | }) 9 | .catch((reason) => { 10 | console.log('Cannot read file1.txt'); 11 | console.log(reason); 12 | }) 13 | .finally(() => { 14 | console.log('finally'); 15 | }); 16 | -------------------------------------------------------------------------------- /JavaScript/8-all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const baseUrl = 'http://localhost:3000'; 4 | 5 | const promises = [ 6 | fetch(baseUrl + '/person'), 7 | fetch(baseUrl + '/'), 8 | fetch(baseUrl + '/city'), 9 | ]; 10 | 11 | Promise.allSettled(promises) 12 | //Promise.all(promises) 13 | .then((values) => { 14 | console.log(values); 15 | }) 16 | .catch((err) => { 17 | console.log(err); 18 | }); 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "patterns", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "lint": "eslint . && prettier -c \"**/*.js\"", 6 | "fix": "eslint . --fix && prettier --write \"**/*.js\"" 7 | }, 8 | "author": "Timur Shemsedinov", 9 | "private": true, 10 | "dependencies": { 11 | "eslint": "^9.12.0", 12 | "eslint-config-metarhia": "^9.1.1", 13 | "prettier": "^3.3.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /JavaScript/0-contract.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const promise = new Promise((resolve, reject) => { 4 | setTimeout(() => { 5 | resolve(100); 6 | //reject(new Error('Custom error')); 7 | }, 0); 8 | }); 9 | 10 | console.dir(promise); // Promise { } 11 | 12 | promise.then( 13 | (value) => { 14 | console.log({ value }); 15 | }, 16 | (error) => { 17 | console.error(error); 18 | }, 19 | ); 20 | -------------------------------------------------------------------------------- /JavaScript/1-callbacks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('node:fs'); 4 | 5 | fs.readFile('file1.txt', 'utf8', (err, data) => { 6 | if (err) console.error(err); 7 | else console.log({ data }); 8 | fs.readFile('file2.txt', 'utf8', (err, data) => { 9 | if (err) console.error(err); 10 | else console.log({ data }); 11 | fs.readFile('file3.txt', 'utf8', (err, data) => { 12 | if (err) console.error(err); 13 | else console.log({ data }); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /JavaScript/6-fetch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const http = require('node:http'); 4 | 5 | const fetch = (url) => 6 | new Promise((resolve, reject) => { 7 | http.get(url, async (res) => { 8 | const code = res.statusCode; 9 | if (code !== 200) { 10 | const err = new Error(`HTTP status code ${code}`); 11 | return void reject(err); 12 | } 13 | const chunks = []; 14 | for await (const chunk of res) chunks.push(chunk); 15 | const data = Buffer.concat(chunks).toString(); 16 | resolve(JSON.parse(data)); 17 | }); 18 | }); 19 | 20 | module.exports = fetch; 21 | -------------------------------------------------------------------------------- /JavaScript/Tasks/2-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Task: rewrite `total` from callbacks contract to promises 4 | // Hint: do not use `async () =>` syntax 5 | 6 | const total = (items, callback) => { 7 | let result = 0; 8 | for (const item of items) { 9 | if (item.price < 0) { 10 | callback(new Error('Negative price is not allowed')); 11 | return; 12 | } 13 | result += item.price; 14 | } 15 | callback(null, result); 16 | }; 17 | 18 | const electronics = [ 19 | { name: 'Laptop', price: 1500 }, 20 | { name: 'Keyboard', price: 100 }, 21 | { name: 'HDMI cable', price: 10 }, 22 | ]; 23 | 24 | total(electronics, (error, money) => { 25 | if (error) console.error({ error }); 26 | else console.log({ money }); 27 | }); 28 | -------------------------------------------------------------------------------- /JavaScript/Tasks/3-chain.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Task: here is given code you can't touch but you need to 4 | // write solution below to sum given prices (wrapped in promises). 5 | // Use Promise.all to resolve promises. 6 | // Use functions: `formatMoney` and `sum` as utilities. 7 | // Additional task: add .catch and .finally blocks in chain 8 | 9 | // Do not touch following code 10 | 11 | const CURRENCY_RATE = 1.09; 12 | 13 | const convert = ({ price }) => Promise.resolve(price * CURRENCY_RATE); 14 | 15 | const electronics = [ 16 | { name: 'Laptop', price: 1500 }, 17 | { name: 'Keyboard', price: 100 }, 18 | { name: 'HDMI cable', price: 10 }, 19 | ]; 20 | 21 | const prices = electronics.map(convert); 22 | 23 | const formatMoney = (x) => parseFloat(x.toFixed(2)); 24 | 25 | const sum = (a, b) => a + b; 26 | 27 | // Write solution below 28 | -------------------------------------------------------------------------------- /JavaScript/Tasks/4-iterate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Task: change `iterate` contract from chainable callbacks 4 | // to Promise (chainable or you can call it with await syntax) 5 | 6 | const iterate = (items) => { 7 | let index = 0; 8 | const chain = { 9 | next: (callback) => { 10 | if (index < items.length) { 11 | callback(items[index++]); 12 | } 13 | return chain; 14 | }, 15 | }; 16 | return chain; 17 | }; 18 | 19 | const electronics = [ 20 | { name: 'Laptop', price: 1500 }, 21 | { name: 'Keyboard', price: 100 }, 22 | { name: 'HDMI cable', price: 10 }, 23 | ]; 24 | 25 | // Use await syntax to get items 26 | iterate(electronics) 27 | .next((item) => { 28 | console.log(item); 29 | }) 30 | .next((item) => { 31 | console.log(item); 32 | }) 33 | .next((item) => { 34 | console.log(item); 35 | }); 36 | -------------------------------------------------------------------------------- /JavaScript/4-catch.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { readFile } = require('node:fs/promises'); 4 | 5 | readFile('file1.txt', 'utf8') 6 | .then( 7 | (data) => { 8 | console.dir({ file1: data }); 9 | return readFile('file2.txt', 'utf8'); 10 | }, 11 | (reason) => { 12 | console.log('1: Cannot read file1.txt'); 13 | console.log(reason); 14 | }, 15 | ) 16 | .catch((reason) => { 17 | console.log('2: Cannot read file1.txt'); 18 | console.log(reason); 19 | }) 20 | .then((data) => { 21 | console.dir({ file2: data }); 22 | return readFile('file3.txt', 'utf8'); 23 | }) 24 | .catch((reason) => { 25 | console.log('Cannot read file2.txt'); 26 | console.log(reason); 27 | }) 28 | .then((data) => { 29 | console.dir({ file3: data }); 30 | }) 31 | .catch((reason) => { 32 | console.log('Cannot read file'); 33 | console.log(reason); 34 | }); 35 | -------------------------------------------------------------------------------- /JavaScript/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const http = require('node:http'); 4 | 5 | const routes = { 6 | '/': (request, callback) => { 7 | callback({ 8 | apiVersion: '1.0', 9 | resources: ['person', 'city'], 10 | }); 11 | }, 12 | 13 | '/person': (request, callback) => { 14 | callback({ 15 | name: 'Alex', 16 | age: 19, 17 | }); 18 | }, 19 | 20 | '/city': (request, callback) => { 21 | callback({ 22 | name: 'Kyiv', 23 | country: 'Ukraine', 24 | }); 25 | }, 26 | }; 27 | 28 | const server = http.createServer((req, res) => { 29 | const handler = routes[req.url]; 30 | if (!handler) { 31 | res.writeHead(404); 32 | res.end('Not found'); 33 | return; 34 | } 35 | handler(req, (result) => { 36 | const json = JSON.stringify(result); 37 | res.writeHead(200, { 'Content-Type': 'application/json' }); 38 | res.end(json); 39 | }); 40 | }); 41 | 42 | server.listen(3000); 43 | -------------------------------------------------------------------------------- /JavaScript/Tasks/1-adapter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Task: create Promise-returning adapter function `totalAsync` 4 | // Do not change function `total` contract, just call `total` from 5 | // `totalAsync` and convert contracts 6 | 7 | // Should not be changed 8 | const total = (items, callback) => { 9 | let result = 0; 10 | for (const item of items) { 11 | if (item.price < 0) { 12 | callback(new Error('Negative price is not allowed')); 13 | return; 14 | } 15 | result += item.price; 16 | } 17 | callback(null, result); 18 | }; 19 | 20 | // const totalAsync = (items) => new Promise... 21 | 22 | const electronics = [ 23 | { name: 'Laptop', price: 1500 }, 24 | { name: 'Keyboard', price: 100 }, 25 | { name: 'HDMI cable', price: 10 }, 26 | ]; 27 | 28 | // Also rewrite call, use .then instead of callback 29 | total(electronics, (error, money) => { 30 | if (error) console.error({ error }); 31 | else console.log({ money }); 32 | }); 33 | -------------------------------------------------------------------------------- /JavaScript/b-thenable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('node:fs'); 4 | 5 | class Thenable { 6 | next = null; 7 | 8 | then(fn) { 9 | this.fn = fn; 10 | this.next = new Thenable(); 11 | return this.next; 12 | } 13 | 14 | resolve(value) { 15 | if (!this.fn) return; 16 | const next = this.fn(value); 17 | if (!next) return; 18 | next.then((value) => { 19 | this.next.resolve(value); 20 | }); 21 | } 22 | } 23 | 24 | // Usage 25 | 26 | const readFile = (filename) => { 27 | const thenable = new Thenable(); 28 | fs.readFile(filename, 'utf8', (err, data) => { 29 | if (err) throw err; 30 | thenable.resolve(data); 31 | }); 32 | return thenable; 33 | }; 34 | 35 | readFile('file1.txt') 36 | .then((data) => { 37 | console.dir({ file1: data }); 38 | return readFile('file2.txt'); 39 | }) 40 | .then((data) => { 41 | console.dir({ file2: data }); 42 | return readFile('file3.txt'); 43 | }) 44 | .then((data) => { 45 | console.dir({ file3: data }); 46 | }); 47 | -------------------------------------------------------------------------------- /JavaScript/Tasks/5-reject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Task: support rejection with an error, if no more items in 4 | // `items` array are available to return with `.next()` 5 | // Change throwing error to returning rejected Promise. 6 | // Catch error with `.catch` or `try/catch` to handle it. 7 | 8 | const iterate = (items) => { 9 | let index = 0; 10 | return { 11 | next: () => 12 | new Promise((fulfill) => { 13 | if (index < items.length) { 14 | return fulfill(items[index++]); 15 | } 16 | throw new Error('No more items to iterate'); 17 | }), 18 | }; 19 | }; 20 | 21 | const electronics = [ 22 | { name: 'Laptop', price: 1500 }, 23 | { name: 'Keyboard', price: 100 }, 24 | { name: 'HDMI cable', price: 10 }, 25 | ]; 26 | 27 | const main = async () => { 28 | const items = iterate(electronics); 29 | const item1 = await items.next(); 30 | console.log(item1); 31 | const item2 = await items.next(); 32 | console.log(item2); 33 | const item3 = await items.next(); 34 | console.log(item3); 35 | const item4 = await items.next(); 36 | console.log(item4); 37 | }; 38 | 39 | main(); 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2025 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 | -------------------------------------------------------------------------------- /JavaScript/d-resolvers.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Need node.js 22 4 | 5 | const sumAsync = (a, b, callback) => { 6 | if (typeof a !== 'number') return; 7 | if (typeof b !== 'number') return; 8 | setImmediate(() => { 9 | callback(a + b); 10 | }); 11 | }; 12 | 13 | // Old approach 14 | 15 | const old = async () => { 16 | let resolve, reject; 17 | const promise = new Promise((resolved, rejected) => { 18 | resolve = resolved; 19 | reject = rejected; 20 | }); 21 | setTimeout(reject, 1000, new Error('Timed out')); 22 | sumAsync(2, 3, resolve); 23 | const result = await promise; 24 | console.log({ result }); 25 | }; 26 | 27 | old(); 28 | 29 | // Alternative approach 30 | 31 | const alternative = async () => { 32 | const promise = new Promise((resolve, reject) => { 33 | sumAsync(4, 5, resolve); 34 | setTimeout(reject, 1000, new Error('Timed out')); 35 | }); 36 | const result = await promise; 37 | console.log({ result }); 38 | }; 39 | 40 | alternative(); 41 | 42 | // New approach 43 | 44 | const modern = async () => { 45 | const { promise, resolve, reject } = Promise.withResolvers(); 46 | setTimeout(reject, 1000, new Error('Timed out')); 47 | sumAsync(6, 7, resolve); 48 | const result = await promise; 49 | console.log({ result }); 50 | }; 51 | 52 | modern(); 53 | -------------------------------------------------------------------------------- /JavaScript/3-promisify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const promisify = 4 | (fn) => 5 | (...args) => 6 | new Promise((resolve, reject) => { 7 | args.push((err, result) => { 8 | if (err) reject(err); 9 | else resolve(result); 10 | }); 11 | fn(...args); 12 | }); 13 | 14 | const fs = require('node:fs'); 15 | 16 | const readFile1 = promisify(fs.readFile); 17 | 18 | readFile1('file1.txt', 'utf8') 19 | .then((data) => { 20 | console.log(data.toString()); 21 | return readFile1('file2.txt', 'utf8'); 22 | }) 23 | .then((data) => { 24 | console.log(data.toString()); 25 | return readFile1('file3.txt', 'utf8'); 26 | }) 27 | .then((data) => { 28 | console.log(data.toString()); 29 | }) 30 | .catch((err) => { 31 | console.log(err); 32 | }); 33 | 34 | const util = require('node:util'); 35 | 36 | const readFile2 = util.promisify(fs.readFile); 37 | 38 | readFile2('file1.txt', 'utf8') 39 | .then((data) => { 40 | console.log(data.toString()); 41 | return readFile2('file2.txt', 'utf8'); 42 | }) 43 | .then((data) => { 44 | console.log(data.toString()); 45 | return readFile2('file3.txt', 'utf8'); 46 | }) 47 | .then((data) => { 48 | console.log(data.toString()); 49 | }) 50 | .catch((err) => { 51 | console.log(err); 52 | }); 53 | -------------------------------------------------------------------------------- /JavaScript/e-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Need node.js 23 4 | 5 | const sumSync = (a, b) => { 6 | if (typeof a !== 'number') throw Error('Expected a: number'); 7 | if (typeof b !== 'number') throw Error('Expected b: number'); 8 | return a + b; 9 | }; 10 | 11 | const sumAsync = async (a, b) => { 12 | if (typeof a !== 'number') throw Error('Expected a: number'); 13 | if (typeof b !== 'number') throw Error('Expected b: number'); 14 | return a + b; 15 | }; 16 | 17 | // Call sync, expected 5 18 | 19 | Promise.try(sumSync, 2, 3) 20 | .then((result) => { 21 | console.log({ result }); 22 | }) 23 | .catch((error) => { 24 | console.error({ error: error.message }); 25 | }) 26 | .finally(() => { 27 | console.log({ finally: '2+3=5' }); 28 | }); 29 | 30 | // Call sync, expected error 31 | 32 | Promise.try(sumSync, '4', 5) 33 | .then((result) => { 34 | console.log({ result }); 35 | }) 36 | .catch((error) => { 37 | console.error({ error: error.message }); 38 | }) 39 | .finally(() => { 40 | console.log({ finally: 'Wrong type: a' }); 41 | }); 42 | 43 | // Call async, expected 13 44 | 45 | Promise.try(sumAsync, 6, 7) 46 | .then((result) => { 47 | console.log({ result }); 48 | }) 49 | .catch((error) => { 50 | console.error({ error: error.message }); 51 | }) 52 | .finally(() => { 53 | console.log({ finally: '6+7=13' }); 54 | }); 55 | 56 | // Call async, expected error 57 | 58 | Promise.try(sumAsync, 8, '9') 59 | .then((result) => { 60 | console.log({ result }); 61 | }) 62 | .catch((error) => { 63 | console.error({ error: error.message }); 64 | }) 65 | .finally(() => { 66 | console.log({ finally: 'Wrong type: b' }); 67 | }); 68 | -------------------------------------------------------------------------------- /JavaScript/2-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Pending 4 | 5 | const promise1 = new Promise((resolve) => { 6 | setTimeout(() => { 7 | resolve('value1'); 8 | }, 0); 9 | }); 10 | console.dir({ promise1 }); // Promise { } 11 | promise1.then(console.log); // 'value1' (delayed) 12 | 13 | // Immediate resolve 14 | 15 | const promise2 = new Promise((resolve) => resolve('value2')); 16 | console.dir({ promise2 }); // Promise { 'value2' } 17 | promise2.then(console.log); // 'value2' 18 | 19 | // Immediate reject 20 | 21 | const promise3 = new Promise((resolve, reject) => { 22 | reject(new Error('I have no value for you')); 23 | }); 24 | console.dir({ promise3 }); // Promise { Error... } 25 | promise3.then(console.log).catch(console.log); // Error... 26 | 27 | // Promise.resolve 28 | 29 | const promise4 = Promise.resolve('value4'); 30 | console.log({ promise4 }); // Promise { 'value4' } 31 | promise4.then(console.log); // 'value4' 32 | 33 | // Promise.reject 34 | 35 | const promise5 = Promise.reject(new Error('I have no value for you')); 36 | console.dir({ promise5 }); // Promise { Error... } 37 | promise5.then(console.log).catch(console.log); // Error... 38 | 39 | // Example with I/O 40 | 41 | const fs = require('node:fs'); 42 | 43 | const readFile = (filename, encoding) => 44 | new Promise((resolve, reject) => 45 | fs.readFile(filename, encoding, (err, data) => { 46 | if (err) reject(err); 47 | else resolve(data.toString()); 48 | }), 49 | ); 50 | 51 | readFile('file1.txt', 'utf8') 52 | .then((data) => { 53 | console.log(data); 54 | return readFile('file2.txt', 'utf8'); 55 | }) 56 | .then((data) => { 57 | console.log(data); 58 | return readFile('file3.txt', 'utf8'); 59 | }) 60 | .then((data) => { 61 | console.log(data); 62 | }); 63 | -------------------------------------------------------------------------------- /JavaScript/c-expirable.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const PROMISE_TIMEOUT = 1000; 4 | 5 | class Expirable { 6 | constructor(executor) { 7 | const promise = new Promise((resolve, reject) => { 8 | executor( 9 | (val) => { 10 | if (this.expired) return; 11 | clearTimeout(this.timer); 12 | resolve(val); 13 | }, 14 | (err) => { 15 | if (this.expired) return; 16 | clearTimeout(this.timer); 17 | reject(err); 18 | }, 19 | ); 20 | this.timer = setTimeout(() => { 21 | this.expired = true; 22 | reject(new Error('Expired')); 23 | }, PROMISE_TIMEOUT); 24 | }); 25 | this.promise = promise; 26 | this.expired = false; 27 | this.timer = null; 28 | return this.promise; 29 | } 30 | } 31 | 32 | // Usage 33 | 34 | new Expirable((resolve) => { 35 | setTimeout(() => { 36 | resolve('Resolved before timeout'); 37 | }, 100); 38 | }) 39 | .then((data) => { 40 | console.dir({ data }); 41 | }) 42 | .catch((error) => { 43 | console.dir({ error: error.message }); 44 | }); 45 | 46 | new Expirable((resolve, reject) => { 47 | setTimeout(() => { 48 | reject(new Error('Something went wrong')); 49 | }, 100); 50 | }) 51 | .then((data) => { 52 | console.dir({ data }); 53 | }) 54 | .catch((error) => { 55 | console.dir({ error: error.message }); 56 | }); 57 | 58 | new Expirable((resolve) => { 59 | setTimeout(() => { 60 | resolve('Never resolved before timeout'); 61 | }, 2000); 62 | }) 63 | .then((data) => { 64 | console.dir({ data }); 65 | }) 66 | .catch((error) => { 67 | console.dir({ error: error.message }); 68 | }); 69 | 70 | new Expirable((resolve, reject) => { 71 | setTimeout(() => { 72 | reject(new Error('Never rejected before timeout')); 73 | }, 2000); 74 | }) 75 | .then((data) => { 76 | console.dir({ data }); 77 | }) 78 | .catch((error) => { 79 | console.dir({ error: error.message }); 80 | }); 81 | -------------------------------------------------------------------------------- /.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 | --------------------------------------------------------------------------------