├── .gitignore ├── eslint.config.js ├── README.md ├── prettier.config.js ├── .editorconfig ├── package.json ├── JavaScript ├── 3-failed.js ├── 4-class.js ├── 2-stateless.js ├── 8-futurify.js ├── 6-promise.js ├── 9-debug.js ├── 7-usage.js ├── 1-lazy.js ├── future.js └── 5-stateless.js └── LICENSE /.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 | module.exports = init; 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Future as Asynchronous Abstraction 2 | 3 | [![Future: Асинхронность на фьючерах без состояния](https://img.youtube.com/vi/22ONv3AGXdk/0.jpg)](https://www.youtube.com/watch?v=22ONv3AGXdk) 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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.0", 13 | "prettier": "^3.3.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /JavaScript/3-failed.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const future = (executor) => ({ 4 | chain(fn) { 5 | return future((resolve, reject) => 6 | this.fork( 7 | (value) => fn(value).fork(resolve), 8 | (error) => reject(error), 9 | ), 10 | ); 11 | }, 12 | 13 | map(fn) { 14 | return this.chain((value) => future.of(fn(value))); 15 | }, 16 | 17 | fork(successed, failed) { 18 | executor(successed, failed); 19 | return this; 20 | }, 21 | }); 22 | 23 | future.of = (value) => future((resolve) => resolve(value)); 24 | 25 | // Usage 26 | 27 | future((resolve, reject) => reject(new Error('Rejected'))) 28 | .map((x) => { 29 | console.log('future1 started'); 30 | return x; 31 | }) 32 | .map((x) => ++x) 33 | .map((x) => x ** 3) 34 | .fork( 35 | (value) => { 36 | console.log('future result', value); 37 | }, 38 | (error) => { 39 | console.log('future failed', error.message); 40 | }, 41 | ); 42 | -------------------------------------------------------------------------------- /JavaScript/4-class.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Future { 4 | #executor; 5 | 6 | constructor(executor) { 7 | this.#executor = executor; 8 | } 9 | 10 | static of(value) { 11 | return new Future((resolve) => resolve(value)); 12 | } 13 | 14 | chain(fn) { 15 | return new Future((resolve, reject) => 16 | this.fork( 17 | (value) => fn(value).fork(resolve, reject), 18 | (error) => reject(error), 19 | ), 20 | ); 21 | } 22 | 23 | map(fn) { 24 | return this.chain((value) => Future.of(fn(value))); 25 | } 26 | 27 | fork(successed, failed) { 28 | this.#executor(successed, failed); 29 | } 30 | } 31 | 32 | // Usage 33 | 34 | Future.of(6) 35 | .map((x) => { 36 | console.log('future1 started'); 37 | return x; 38 | }) 39 | .map((x) => ++x) 40 | .map((x) => x ** 3) 41 | .fork( 42 | (value) => { 43 | console.log('future result', value); 44 | }, 45 | (error) => { 46 | console.log('future failed', error.message); 47 | }, 48 | ); 49 | -------------------------------------------------------------------------------- /JavaScript/2-stateless.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const future = (executor) => ({ 4 | chain(fn) { 5 | return future((resolve) => this.fork((value) => fn(value).fork(resolve))); 6 | }, 7 | 8 | map(fn) { 9 | return this.chain((value) => future.of(fn(value))); 10 | }, 11 | 12 | fork(successed) { 13 | executor(successed); 14 | return this; 15 | }, 16 | }); 17 | 18 | future.of = (value) => future((resolve) => resolve(value)); 19 | 20 | // Usage 21 | 22 | const future1 = future((r) => r(5)) 23 | .map((x) => { 24 | console.log('future1 started'); 25 | return x; 26 | }) 27 | .map((x) => ++x) 28 | .map((x) => x ** 3) 29 | .fork((x) => { 30 | console.log('future1 result', x); 31 | }); 32 | 33 | console.dir({ future1 }); 34 | 35 | const promise1 = Promise.resolve(6) 36 | .then((x) => { 37 | console.log('promise1 started'); 38 | return x; 39 | }) 40 | .then((x) => ++x) 41 | .then((x) => x ** 3) 42 | .then((x) => { 43 | console.log('promise1 result', x); 44 | }); 45 | 46 | console.dir({ promise1 }); 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 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/8-futurify.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('node:fs'); 4 | 5 | class Future { 6 | #executor; 7 | 8 | constructor(executor) { 9 | this.#executor = executor; 10 | } 11 | 12 | static of(value) { 13 | return new Future((resolve) => resolve(value)); 14 | } 15 | 16 | chain(fn) { 17 | return new Future((resolve, reject) => 18 | this.fork( 19 | (value) => fn(value).fork(resolve, reject), 20 | (error) => reject(error), 21 | ), 22 | ); 23 | } 24 | 25 | map(fn) { 26 | return this.chain((value) => Future.of(fn(value))); 27 | } 28 | 29 | fork(successed, failed) { 30 | this.#executor(successed, failed); 31 | } 32 | } 33 | 34 | const futurify = 35 | (fn) => 36 | (...args) => 37 | new Future((resolve, reject) => { 38 | fn(...args, (err, data) => { 39 | if (err) reject(err); 40 | else resolve(data); 41 | }); 42 | }); 43 | 44 | // Usage 45 | 46 | const readFile = (name, callback) => fs.readFile(name, 'utf8', callback); 47 | const futureFile = futurify(readFile); 48 | 49 | futureFile('8-futurify.js') 50 | .map((x) => x.length) 51 | .fork((x) => console.log('File size:', x)); 52 | -------------------------------------------------------------------------------- /JavaScript/6-promise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Future { 4 | #executor; 5 | 6 | constructor(executor) { 7 | this.#executor = executor; 8 | } 9 | 10 | static of(value) { 11 | return new Future((resolve) => resolve(value)); 12 | } 13 | 14 | chain(fn) { 15 | return new Future((resolve, reject) => 16 | this.fork( 17 | (value) => fn(value).fork(resolve, reject), 18 | (error) => reject(error), 19 | ), 20 | ); 21 | } 22 | 23 | map(fn) { 24 | return this.chain((value) => Future.of(fn(value))); 25 | } 26 | 27 | fork(successed, failed) { 28 | this.#executor(successed, failed); 29 | } 30 | 31 | promise() { 32 | return new Promise((resolve, reject) => { 33 | this.fork( 34 | (value) => resolve(value), 35 | (error) => reject(error), 36 | ); 37 | }); 38 | } 39 | } 40 | 41 | // Usage 42 | 43 | const main = async () => { 44 | const value = await Future.of(6) 45 | .map((x) => { 46 | console.log('future1 started'); 47 | return x; 48 | }) 49 | .map((x) => ++x) 50 | .map((x) => x ** 3) 51 | .promise(); 52 | 53 | console.log('result', value); 54 | }; 55 | 56 | main(); 57 | -------------------------------------------------------------------------------- /JavaScript/9-debug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let i = 0; 4 | 5 | class Future { 6 | #executor; 7 | 8 | constructor(executor) { 9 | this.id = i++; 10 | console.log(`new Future ${this.id}`); 11 | this.#executor = executor; 12 | } 13 | 14 | static of(value) { 15 | return new Future((resolve) => resolve(value)); 16 | } 17 | 18 | chain(fn) { 19 | console.log(`chain ${this.id}`); 20 | return new Future((resolve, reject) => 21 | this.fork( 22 | (value) => { 23 | console.log(`resolve ${this.id}`); 24 | fn(value).fork(resolve, reject); 25 | }, 26 | (error) => reject(error), 27 | ), 28 | ); 29 | } 30 | 31 | map(fn) { 32 | console.log(`map ${this.id}`); 33 | return this.chain((value) => { 34 | console.log(`map.chain ${this.id}`); 35 | return Future.of(fn(value)); 36 | }); 37 | } 38 | 39 | fork(successed, failed) { 40 | console.log(`fork ${this.id}`); 41 | this.#executor(successed, failed); 42 | } 43 | } 44 | 45 | // Usage 46 | 47 | Future.of(5) 48 | .map((x) => ++x) 49 | .map((x) => x ** 3) 50 | .map((x) => x * 2) 51 | .fork((value) => console.log(`Result ${value}`)); 52 | -------------------------------------------------------------------------------- /JavaScript/7-usage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('node:fs'); 4 | 5 | class Future { 6 | #executor; 7 | 8 | constructor(executor) { 9 | this.#executor = executor; 10 | } 11 | 12 | static of(value) { 13 | return new Future((resolve) => resolve(value)); 14 | } 15 | 16 | chain(fn) { 17 | return new Future((resolve, reject) => 18 | this.fork( 19 | (value) => fn(value).fork(resolve, reject), 20 | (error) => reject(error), 21 | ), 22 | ); 23 | } 24 | 25 | map(fn) { 26 | return this.chain((value) => Future.of(fn(value))); 27 | } 28 | 29 | fork(successed, failed) { 30 | this.#executor(successed, failed); 31 | } 32 | } 33 | 34 | // Usage 35 | 36 | const futureFile = (name) => 37 | new Future((resolve, reject) => { 38 | fs.readFile(name, 'utf8', (err, data) => { 39 | console.log('read'); 40 | if (err) reject(err); 41 | else resolve(data); 42 | }); 43 | }); 44 | 45 | const future = futureFile('7-usage.js'); 46 | const size = future.map((x) => x.length); 47 | const lines = future.map((x) => x.split('\n').length); 48 | 49 | size.fork((x) => console.log('File size:', x)); 50 | lines.fork((x) => console.log('Line count:', x)); 51 | -------------------------------------------------------------------------------- /JavaScript/1-lazy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const id = (x) => x; 4 | 5 | const future = (value) => { 6 | let mapper = null; 7 | 8 | const instance = {}; 9 | 10 | const map = (fn) => { 11 | mapper = fn; 12 | return future(instance); 13 | }; 14 | 15 | const fork = (successed) => { 16 | const finish = (product) => { 17 | const transform = mapper ?? id; 18 | const result = transform(product); 19 | if (successed) successed(result); 20 | }; 21 | if (value.fork) return void value.fork(finish); 22 | finish(value); 23 | }; 24 | 25 | return Object.assign(instance, { map, fork }); 26 | }; 27 | 28 | // Usage 29 | 30 | const future1 = future(5) 31 | .map((x) => { 32 | console.log('future1 started'); 33 | return x; 34 | }) 35 | .map((x) => ++x) 36 | .map((x) => x ** 3) 37 | .map((x) => { 38 | console.log('future1 result', x); 39 | }); 40 | 41 | console.dir({ future1 }); 42 | //future1.fork(); 43 | 44 | const promise1 = Promise.resolve(6) 45 | .then((x) => { 46 | console.log('promise1 started'); 47 | return x; 48 | }) 49 | .then((x) => ++x) 50 | .then((x) => x ** 3) 51 | .then((x) => { 52 | console.log('promise1 result', x); 53 | }); 54 | 55 | console.dir({ promise1 }); 56 | -------------------------------------------------------------------------------- /JavaScript/future.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Future { 4 | #executor; 5 | 6 | constructor(executor) { 7 | this.#executor = executor; 8 | } 9 | 10 | static of(value) { 11 | return new Future((resolve) => resolve(value)); 12 | } 13 | 14 | chain(fn) { 15 | return new Future((resolve, reject) => 16 | this.fork( 17 | (value) => fn(value).fork(resolve, reject), 18 | (error) => reject(error), 19 | ), 20 | ); 21 | } 22 | 23 | map(fn) { 24 | return new Future((resolve, reject) => 25 | this.fork( 26 | (value) => 27 | new Future((resolve, reject) => { 28 | try { 29 | resolve(fn(value)); 30 | } catch (error) { 31 | reject(error); 32 | } 33 | }).fork(resolve, reject), 34 | (error) => reject(error), 35 | ), 36 | ); 37 | } 38 | 39 | fork(successed, failed) { 40 | this.#executor(successed, failed); 41 | } 42 | 43 | toPromise() { 44 | const { promise, resolve, reject } = Promise.withResolvers(); 45 | this.fork(resolve, reject); 46 | return promise; 47 | } 48 | 49 | toThenable() { 50 | const then = (resolve, reject) => { 51 | this.fork(resolve, reject); 52 | }; 53 | return { then }; 54 | } 55 | } 56 | 57 | const futurify = 58 | (fn) => 59 | (...args) => 60 | new Future((resolve, reject) => { 61 | fn(...args, (err, data) => { 62 | if (err) reject(err); 63 | else resolve(data); 64 | }); 65 | }); 66 | 67 | module.exports = { Future, futurify }; 68 | -------------------------------------------------------------------------------- /JavaScript/5-stateless.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class Future { 4 | #executor; 5 | 6 | constructor(executor) { 7 | this.#executor = executor; 8 | } 9 | 10 | static of(value) { 11 | return new Future((resolve) => resolve(value)); 12 | } 13 | 14 | chain(fn) { 15 | return new Future((resolve, reject) => 16 | this.fork( 17 | (value) => fn(value).fork(resolve, reject), 18 | (error) => reject(error), 19 | ), 20 | ); 21 | } 22 | 23 | map(fn) { 24 | return this.chain((value) => Future.of(fn(value))); 25 | } 26 | 27 | fork(successed, failed) { 28 | this.#executor(successed, failed); 29 | } 30 | } 31 | 32 | // Usage 33 | 34 | const f1 = Future.of(6); 35 | const f2 = f1.map((x) => ++x); 36 | const f3 = f2.map((x) => x ** 3); 37 | const f4 = f1.map((x) => x * 2); 38 | 39 | /* 40 | F(6) 41 | | 42 | f1---\ 43 | | | 44 | f2 f4 45 | | 46 | f3 47 | */ 48 | 49 | f1.fork((x) => console.log('f1 fork1', x)); 50 | f1.fork((x) => console.log('f1 fork2', x)); 51 | f2.fork((x) => console.log('f2 fork1', x)); 52 | f2.fork((x) => console.log('f2 fork2', x)); 53 | f3.fork((x) => console.log('f3 fork1', x)); 54 | f3.fork((x) => console.log('f3 fork2', x)); 55 | f4.fork((x) => console.log('f4 fork1', x)); 56 | f4.fork((x) => console.log('f4 fork2', x)); 57 | 58 | console.log('\nChange initial value of chain:'); 59 | { 60 | const source = (r) => r(source.value); 61 | source.value = 2; 62 | 63 | const f1 = new Future(source) 64 | .map((x) => ++x) 65 | .map((x) => x ** 3) 66 | .map((x) => x * 2); 67 | 68 | f1.fork((x) => console.log('fork1', x)); 69 | source.value = 3; 70 | f1.fork((x) => console.log('fork2', x)); 71 | source.value = 4; 72 | f1.fork((x) => console.log('fork3', x)); 73 | source.value = 5; 74 | f1.fork((x) => console.log('fork4', x)); 75 | } 76 | --------------------------------------------------------------------------------