├── .gitignore ├── .npmrc ├── .travis.yml ├── README.md ├── package.json └── src ├── 00-procedural.js ├── 01-procedural-with-reuse.js ├── 02-oo-es6.js ├── 02-oo-plain-prototype-object.js ├── 02-oo-with-array-methods.js ├── 02-oo.js ├── 03-functional-compose.js ├── 03-functional-lispyscript.js ├── 03-functional-lispyscript.ls ├── 03-functional-recursive.js ├── 03-functional-with-lodash.js ├── 03-functional-with-ramda.js ├── 03-functional.js ├── 03-point-free.js ├── 04-lazy.js ├── 05-promises.js ├── 06-promises-sequence.js ├── 07-events.js ├── 08-events-decoupled.js ├── 09-step-emitters.js ├── 10-reactive.js ├── 11-generators.js ├── 12-streams.js ├── 13-transducers.js ├── 14-dependency-injection.js ├── 15-immutable-using-immutable.js ├── 15-immutable-using-seamless.js ├── 15-immutable-with-redux.js ├── 16-async-await.js ├── 17-asm.js └── 18-maybe-monad.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - '6' 5 | branches: 6 | only: 7 | - master 8 | before_install: 9 | - npm install -g npm@3 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # javascript-journey 2 | 3 | > Companion source code to [Journey from procedural to reactive JavaScript with stops][post] 4 | 5 | [![Build status][ci-image] ][ci-url] 6 | 7 | Problem: given a list of numbers, multiply each one by a constant 8 | and print the result. 9 | 10 | ```js 11 | var numbers = [3, 1, 7]; 12 | var constant = 2; 13 | // expected output [6, 2, 14] 14 | ``` 15 | 16 | To install and run 17 | 18 | git clone git@github.com:bahmutov/javascript-journey.git 19 | cd javascript-journey 20 | npm install 21 | 22 | Then execute individual files using `node src/` command. The following 23 | implementations are available 24 | 25 | * [procedural](src/00-procedural.js) 26 | * [procedural with individual functions](src/01-procedural-with-reuse.js) 27 | * [object-oriented](src/02-oo.js) 28 | * [object-oriented using plain prototype object](src/02-oo-plain-prototype-object.js) 29 | * [object-oriented using Array methods](src/02-oo-with-array-methods.js) 30 | * [object-oriented using ES6 class](src/02-oo-es6.js) 31 | * [functional](src/03-functional.js) 32 | * [functional recursive](src/03-functional-recursive.js) 33 | * [functional compose](src/03-functional-compose.js) 34 | * [functional using lodash](src/03-functional-with-lodash.js) 35 | * [functional using Ramda](src/03-functional-with-ramda.js) 36 | * [functional using LispyScript](src/03-functional-lispyscript.ls), see [reference](http://lispyscript.com/) 37 | * [point-free](src/03-point-free.js) 38 | * [lazy async sequence](src/04-lazy.js) 39 | * [promises in parallel](src/05-promises.js) 40 | * [promises in sequence](src/06-promises-sequence.js) 41 | * [events](src/07-events.js) 42 | * [events with decoupled code](src/08-events-decoupled.js) 43 | * [each step creates new emitter](src/09-step-emitters.js) 44 | * [reactive](src/10-reactive.js) 45 | * [generators](src/11-generators.js) 46 | * [streams](src/12-streams.js) 47 | * [transducers](src/13-transducers.js) 48 | * [dependency injection](src/14-dependency-injection.js) 49 | * [immutable data using seamless-immutable](src/15-immutable-using-seamless.js) 50 | * [immutable data using immutable.js](src/15-immutable-using-immutable.js) 51 | * [immutable using Redux pattern](src/15-immutable-with-redux.js) 52 | * [async await](src/16-async-await.js) 53 | * [asm.js](src/17-asm.js) 54 | * [Maybe monad](src/18-maybe-monad.js) 55 | 56 | [post]: http://glebbahmutov.com/blog/journey-from-procedural-to-reactive-javascript-with-stops/ 57 | 58 | ### Small print 59 | 60 | Author: Gleb Bahmutov © 2015 61 | 62 | * [@bahmutov](https://twitter.com/bahmutov) 63 | * [glebbahmutov.com](http://glebbahmutov.com) 64 | * [blog](http://glebbahmutov.com/blog/) 65 | 66 | License: MIT - do anything with the code, but don't blame me if it does not work. 67 | 68 | Spread the word: tweet, star on github, etc. 69 | 70 | Support: if you find any problems with this module, email / tweet / 71 | [open issue](https://github.com/bahmutov/javascript-journey/issues) on Github 72 | 73 | ## MIT License 74 | 75 | Copyright (c) 2015 Gleb Bahmutov 76 | 77 | Permission is hereby granted, free of charge, to any person 78 | obtaining a copy of this software and associated documentation 79 | files (the "Software"), to deal in the Software without 80 | restriction, including without limitation the rights to use, 81 | copy, modify, merge, publish, distribute, sublicense, and/or sell 82 | copies of the Software, and to permit persons to whom the 83 | Software is furnished to do so, subject to the following 84 | conditions: 85 | 86 | The above copyright notice and this permission notice shall be 87 | included in all copies or substantial portions of the Software. 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 91 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 92 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 93 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 95 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 96 | OTHER DEALINGS IN THE SOFTWARE. 97 | 98 | [ci-image]: https://travis-ci.org/bahmutov/javascript-journey.svg?branch=master 99 | [ci-url]: https://travis-ci.org/bahmutov/javascript-journey 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-journey", 3 | "version": "1.0.0", 4 | "description": "Source examples for blog post Journey from procedural to reactive JavaScript with stops", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./.git/hooks/pre-commit -f", 8 | "commit": "git-issues && commit-wizard", 9 | "issues": "git-issues", 10 | "lispy": "./node_modules/lispyscript/bin/lispy.js src/03-functional-lispyscript.ls && node src/03-functional-lispyscript.js" 11 | }, 12 | "engines": { 13 | "npm": "3" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/bahmutov/javascript-journey.git" 18 | }, 19 | "keywords": [ 20 | "example", 21 | "javascript", 22 | "blog", 23 | "post" 24 | ], 25 | "author": "Gleb Bahmutov ", 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/bahmutov/javascript-journey/issues" 29 | }, 30 | "homepage": "https://github.com/bahmutov/javascript-journey", 31 | "private": true, 32 | "config": { 33 | "pre-git": { 34 | "commit-msg": "simple", 35 | "pre-commit": [ 36 | "node src/00-procedural.js", 37 | "node src/01-procedural-with-reuse.js", 38 | "node src/02-oo.js", 39 | "node src/02-oo-plain-prototype-object.js", 40 | "node src/02-oo-with-array-methods.js", 41 | "node src/03-functional.js", 42 | "node src/03-functional-recursive.js", 43 | "node src/03-functional-compose.js", 44 | "node src/03-functional-with-lodash.js", 45 | "node src/03-functional-with-ramda.js", 46 | "npm run lispy", 47 | "node src/03-point-free.js", 48 | "node src/04-lazy.js", 49 | "node src/05-promises.js", 50 | "node src/06-promises-sequence.js", 51 | "node src/07-events.js", 52 | "node src/08-events-decoupled.js", 53 | "node src/09-step-emitters.js", 54 | "node src/10-reactive.js", 55 | "node --harmony src/11-generators.js", 56 | "node src/12-streams.js", 57 | "node src/13-transducers.js", 58 | "node src/14-dependency-injection.js", 59 | "node src/15-immutable-using-immutable.js", 60 | "node src/15-immutable-using-seamless.js", 61 | "node src/15-immutable-with-redux.js", 62 | "node src/16-async-await.js", 63 | "node src/17-asm.js", 64 | "node src/18-maybe-monad.js" 65 | ], 66 | "pre-push": [], 67 | "post-commit": [], 68 | "post-merge": [] 69 | } 70 | }, 71 | "devDependencies": { 72 | "git-issues": "1.1.0", 73 | "pre-git": "3.9.1" 74 | }, 75 | "czConfig": { 76 | "path": "node_modules/cz-conventional-changelog" 77 | }, 78 | "dependencies": { 79 | "asyncawait": "1.0.1", 80 | "data.maybe": "1.2.2", 81 | "heroin": "0.5.0", 82 | "immutable": "3.7.5", 83 | "lazy.js": "0.4.2", 84 | "lispyscript": "^1.0.1", 85 | "lodash": "3.10.1", 86 | "q": "1.4.1", 87 | "ramda": "0.18.0", 88 | "redux": "3.0.4", 89 | "rx": "4.0.6", 90 | "seamless-immutable": "4.0.2", 91 | "transducers-js": "0.4.174" 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/00-procedural.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | var k = 0; 5 | for(k = 0; k < numbers.length; k += 1) { 6 | console.log(numbers[k] * constant); 7 | } 8 | -------------------------------------------------------------------------------- /src/01-procedural-with-reuse.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function processNumber(n, constant) { 8 | console.log(mul(n, constant)); 9 | } 10 | for(var k = 0; k < numbers.length; k += 1) { 11 | processNumber(numbers[k], constant); 12 | } 13 | // 6 2 14 14 | -------------------------------------------------------------------------------- /src/02-oo-es6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // to run use Node 5 4 | // nvm use 5 5 | // node 02-oo-es6.js 6 | 7 | var numbers = [3, 1, 7]; 8 | var constant = 2; 9 | 10 | class NumberMultiplier { 11 | setNumbers(numbers) { 12 | this.numbers = numbers; 13 | return this; 14 | } 15 | 16 | multiply(constant) { 17 | for (var k = 0; k < this.numbers.length; k += 1) { 18 | this.numbers[k] = constant * this.numbers[k]; 19 | } 20 | return this; 21 | } 22 | 23 | print() { 24 | console.log(this.numbers); 25 | return this; 26 | } 27 | } 28 | 29 | new NumberMultiplier() 30 | .setNumbers(numbers) 31 | .multiply(constant) 32 | .print(); 33 | // [ 6, 2, 14 ] 34 | -------------------------------------------------------------------------------- /src/02-oo-plain-prototype-object.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | // place all methods into a plain object 5 | var NumberMultiplier = { 6 | setNumbers: function (numbers) { 7 | this.numbers = numbers; 8 | return this; 9 | }, 10 | multiply: function (constant) { 11 | for (var k = 0; k < this.numbers.length; k += 1) { 12 | this.numbers[k] = constant * this.numbers[k]; 13 | } 14 | return this; 15 | }, 16 | print: function () { 17 | console.log(this.numbers); 18 | return this; 19 | } 20 | }; 21 | // create object using a plain object as prototype 22 | // and avoiding "new" keyword 23 | var numberMultiplier = Object.create(NumberMultiplier); 24 | numberMultiplier 25 | .setNumbers(numbers) 26 | .multiply(constant) 27 | .print(); 28 | // [ 6, 2, 14 ] 29 | -------------------------------------------------------------------------------- /src/02-oo-with-array-methods.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | numbers.map(function (n) { 11 | return mul(n, constant); 12 | }).forEach(print); 13 | // 6 2 14 14 | -------------------------------------------------------------------------------- /src/02-oo.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | // constructor 5 | function NumberMultiplier() {} 6 | 7 | // prototype methods 8 | NumberMultiplier.prototype.setNumbers = function (numbers) { 9 | this.numbers = numbers; 10 | return this; 11 | }; 12 | 13 | NumberMultiplier.prototype.multiply = function (constant) { 14 | for (var k = 0; k < this.numbers.length; k += 1) { 15 | this.numbers[k] = constant * this.numbers[k]; 16 | } 17 | return this; 18 | }; 19 | 20 | NumberMultiplier.prototype.print = function () { 21 | console.log(this.numbers); 22 | return this; 23 | }; 24 | 25 | new NumberMultiplier() 26 | .setNumbers(numbers) 27 | .multiply(constant) 28 | .print(); 29 | // [ 6, 2, 14 ] 30 | -------------------------------------------------------------------------------- /src/03-functional-compose.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | 11 | var byConstant = mul.bind(null, constant); 12 | 13 | function forEach(array, cb) { 14 | for (var k = 0; k < array.length; k += 1) { 15 | cb(array[k]); 16 | } 17 | } 18 | 19 | // mathematical composition of functions 20 | // (f * g) (x) = f(g(x)) 21 | function compose(f, g) { 22 | return function (x) { 23 | return f(g(x)); 24 | } 25 | } 26 | 27 | var process = compose(print, byConstant); 28 | forEach(numbers, process); 29 | // 6 2 14 30 | -------------------------------------------------------------------------------- /src/03-functional-lispyscript.js: -------------------------------------------------------------------------------- 1 | // Generated by LispyScript v1.0.0 2 | var numbers = [ 3 | 3, 4 | 1, 5 | 7 6 | ]; 7 | var constant = 2; 8 | var mul = function(a,b) { 9 | return (a * b); 10 | }; 11 | var byConstant = Function.prototype.bind.call(mul,null,constant); 12 | var process = function(x) { 13 | return console.log(byConstant(x)); 14 | }; 15 | (numbers).forEach(process); 16 | -------------------------------------------------------------------------------- /src/03-functional-lispyscript.ls: -------------------------------------------------------------------------------- 1 | ; for something completely different, here is 2 | ; LispyScript http://lispyscript.com/ implementation 3 | (var numbers (array 3 1 7)) 4 | (var constant 2) 5 | (var mul (function (a b) (* a b))) 6 | ; partial application 7 | (var byConstant ( 8 | Function.prototype.bind.call mul null constant)) 9 | (var process (function (x) ( 10 | console.log (byConstant x)))) 11 | ; iteration over an array 12 | (each numbers process) 13 | ; 6 14 | ; 2 15 | ; 14 16 | ; see the generated JavaScript file with same name 17 | ; in this folder 18 | -------------------------------------------------------------------------------- /src/03-functional-recursive.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | 11 | var byConstant = mul.bind(null, constant); 12 | 13 | function head(array){ 14 | return array[0]; 15 | } 16 | 17 | function tail(array){ 18 | return array.slice(1); 19 | } 20 | 21 | function map(array, cb) { 22 | if (array.length === 0){ 23 | return []; 24 | } 25 | 26 | return [cb(head(array))].concat(map(tail(array), cb)); 27 | } 28 | 29 | function forEach(array, cb) { 30 | if (array.length == 0){ 31 | return; 32 | } 33 | 34 | cb(head(array)); 35 | forEach(tail(array), cb); 36 | } 37 | 38 | forEach(map(numbers, byConstant), print); 39 | // 6 2 14 40 | -------------------------------------------------------------------------------- /src/03-functional-with-lodash.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | var _ = require('lodash'); 5 | 6 | _.map(numbers, number => console.log(number * constant)); 7 | -------------------------------------------------------------------------------- /src/03-functional-with-ramda.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function print(n) { 5 | console.log(n); 6 | } 7 | 8 | // notice built-in currying in Ramda methods 9 | var R = require('ramda'); 10 | var byConstant = R.multiply(constant); 11 | var mapByConstant = R.map(byConstant); 12 | var printEach = R.forEach(print); 13 | 14 | // R.pipe is the inverse of R.compose 15 | var algorithm = R.pipe(mapByConstant, printEach); 16 | algorithm(numbers); 17 | // 6 2 14 18 | 19 | // even shorter for this simple case 20 | // is to rely on curried functions 21 | R.pipe( 22 | R.map(R.multiply(constant)), 23 | R.forEach(print) 24 | )(numbers); 25 | // 6 2 14 26 | -------------------------------------------------------------------------------- /src/03-functional.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | 11 | var byConstant = mul.bind(null, constant); 12 | 13 | function map(array, cb) { 14 | var result = []; 15 | for (var k = 0; k < array.length; k += 1) { 16 | result[k] = cb(array[k]); 17 | } 18 | return result; 19 | } 20 | 21 | function forEach(array, cb) { 22 | for (var k = 0; k < array.length; k += 1) { 23 | cb(array[k]); 24 | } 25 | } 26 | 27 | forEach(map(numbers, byConstant), print); 28 | // 6 2 14 29 | -------------------------------------------------------------------------------- /src/03-point-free.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | 8 | // standard practice 9 | // iterator callbacks pass arguments to specific functions 10 | numbers 11 | .map(function (x) { 12 | return mul(constant, x); 13 | }).forEach(function (k) { 14 | console.log(k); 15 | }); 16 | // 6 2 14 17 | 18 | // make multiplication point-free 19 | numbers 20 | .map(mul.bind(null, constant)) 21 | .forEach(function (k) { 22 | console.log(k); 23 | }); 24 | // 6 2 14 25 | 26 | // we cannot simple pass console.log as forEach() callback 27 | // because console.log will print ALL arguments, not just the first one 28 | // while forEach() passes (item, index, array) 29 | numbers 30 | .map(mul.bind(null, constant)) 31 | .forEach(console.log); 32 | /* 33 | 6 0 [ 6, 2, 14 ] 34 | 2 1 [ 6, 2, 14 ] 35 | 14 2 [ 6, 2, 14 ] 36 | */ 37 | 38 | // point-free is about adopting callback signatures 39 | function first(fn) { 40 | return function (x) { 41 | return fn(x); 42 | }; 43 | } 44 | numbers 45 | .map(mul.bind(null, constant)) 46 | .forEach(first(console.log)); 47 | // 6 2 14 48 | -------------------------------------------------------------------------------- /src/04-lazy.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | var byConstant = mul.bind(null, constant); 11 | 12 | var lazy = require('lazy.js'); 13 | lazy(numbers) 14 | .async(1000) // 1000ms = 1 sec 15 | .map(byConstant) 16 | .each(print); 17 | // sleeps 1 second between printing each number 18 | -------------------------------------------------------------------------------- /src/05-promises.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | var Q = require('q'); 5 | function asyncMul(a, b) { 6 | return Q(a * b).delay(1000); 7 | } 8 | function print(n) { 9 | console.log(n); 10 | } 11 | var byConstantAsync = asyncMul.bind(null, constant); 12 | var promiseToMulNumber = function (n) { 13 | return byConstantAsync(n); 14 | }; 15 | 16 | console.log('running all async operations in parallel'); 17 | Q.all(numbers.map(promiseToMulNumber)) 18 | .then(print) 19 | .done(); 20 | // sleeps 1 second 21 | // then prints [6, 2, 14] 22 | -------------------------------------------------------------------------------- /src/06-promises-sequence.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | var Q = require('q'); 5 | function asyncMul(a, b) { 6 | return Q(a * b).delay(1000); 7 | } 8 | function print(n) { 9 | console.log(n); 10 | } 11 | var byConstantAsync = asyncMul.bind(null, constant); 12 | var promiseToMulNumber = function (n) { 13 | return byConstantAsync(n); 14 | }; 15 | 16 | var mulAndPrint = function (n) { 17 | return function () { 18 | return byConstantAsync(n).then(print); 19 | }; 20 | }; 21 | 22 | console.log('running one async action at a time'); 23 | numbers.map(mulAndPrint) 24 | .reduce(Q.when, Q()) 25 | .done(); 26 | // sleeps 1 second 27 | // 6 28 | // sleeps 1 second 29 | // 2 30 | // ... 31 | -------------------------------------------------------------------------------- /src/07-events.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | var byConstant = mul.bind(null, constant); 11 | 12 | var _ = require('lodash'); 13 | var events = require('events'); 14 | var numberEmitter = new events.EventEmitter(); 15 | numberEmitter.on('number', _.compose(print, byConstant)); 16 | 17 | var k = 0; 18 | var ref = setInterval(function () { 19 | numberEmitter.emit('number', numbers[k++]); 20 | if (k >= numbers.length) { 21 | clearInterval(ref); 22 | } 23 | }, 1000); 24 | // prints 6, 2 14 with 1 second intervals 25 | -------------------------------------------------------------------------------- /src/08-events-decoupled.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | var byConstant = mul.bind(null, constant); 11 | 12 | var _ = require('lodash'); 13 | var lazy = require('lazy.js'); 14 | var events = require('events'); 15 | 16 | function source(list) { 17 | var eventEmitter = new events.EventEmitter(); 18 | lazy(list) 19 | .async(1000) 20 | .each(_.bind(eventEmitter.emit, eventEmitter, 'step')); 21 | return { 22 | on: function (cb) { 23 | eventEmitter.on('step', cb); 24 | } 25 | }; 26 | } 27 | source(numbers) 28 | .on(_.compose(print, byConstant)); 29 | 30 | // prints 6, 2 14 with 1 second intervals 31 | -------------------------------------------------------------------------------- /src/09-step-emitters.js: -------------------------------------------------------------------------------- 1 | // reusable step emitter "library" 2 | var _ = require('lodash'); 3 | var events = require('events'); 4 | var stepEmitter = { 5 | // apply given callback to the value received 6 | // then emit the result through NEW step emitter 7 | map: function (cb) { 8 | var emitter = new events.EventEmitter(); 9 | this.on('step', function (value) { 10 | var mappedValue = cb(value); 11 | emitter.emit('step', mappedValue); 12 | }); 13 | return _.extend(emitter, stepEmitter); 14 | }, 15 | // apply given callback to to the value received 16 | // then emit the value again through NEW step emitter 17 | forEach: function (cb) { 18 | var emitter = new events.EventEmitter(); 19 | this.on('step', function (value) { 20 | cb(value); 21 | emitter.emit('step', value); 22 | }); 23 | return _.extend(emitter, stepEmitter); 24 | }, 25 | // returns a new step emitter that accumulates N items 26 | // emits the entire array with N items as single argument 27 | buffer: function (n) { 28 | var received = []; 29 | var emitter = new events.EventEmitter(); 30 | this.on('step', function (value) { 31 | received.push(value); 32 | if (received.length === n) { 33 | emitter.emit('step', received); 34 | received = []; 35 | } 36 | }); 37 | return _.extend(emitter, stepEmitter); 38 | } 39 | }; 40 | 41 | // returns stepEmitter = event emitter + stepEmitter methods 42 | function source(list) { 43 | var lazy = require('lazy.js'); 44 | 45 | var eventEmitter = new events.EventEmitter(); 46 | lazy(list) 47 | .async(1000) 48 | .each(_.bind(eventEmitter.emit, eventEmitter, 'step')); 49 | 50 | return _.extend(eventEmitter, stepEmitter); 51 | } 52 | 53 | // our "user" functions, take 4 numbers for clarity 54 | var numbers = [3, 1, 7, 5]; 55 | var constant = 2; 56 | 57 | function mul(a, b) { 58 | return a * b; 59 | } 60 | function print(n) { 61 | console.log(n); 62 | } 63 | var byConstant = mul.bind(null, constant); 64 | 65 | // use source library 66 | source(numbers) 67 | .map(byConstant) 68 | .buffer(2) 69 | .forEach(print); 70 | // sleeps 2 seconds 71 | // [ 6, 2 ] 72 | // sleeps 2 seconds 73 | // [ 14, 10 ] 74 | -------------------------------------------------------------------------------- /src/10-reactive.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | var byConstant = mul.bind(null, constant); 11 | 12 | var Rx = require('rx'); 13 | var timeEvents = Rx.Observable 14 | .interval(1000) 15 | .timeInterval(); 16 | 17 | var numberEvents = Rx.Observable 18 | .fromArray(numbers); 19 | 20 | function pickSecondValue(first, second) { 21 | return second; 22 | } 23 | Rx.Observable.zip(timeEvents, numberEvents, pickSecondValue) 24 | .map(byConstant) 25 | .subscribe(print); 26 | // prints 6 2 14 with 1 second intervals 27 | -------------------------------------------------------------------------------- /src/11-generators.js: -------------------------------------------------------------------------------- 1 | // run with --harmony flag 2 | var numbers = [3, 1, 7]; 3 | var constant = 2; 4 | 5 | function mul(a, b) { 6 | return a * b; 7 | } 8 | function print(n) { 9 | console.log(n); 10 | } 11 | var byConstant = mul.bind(null, constant); 12 | 13 | function* numberGenerator() { 14 | for (var k = 0; k < numbers.length; k += 1) { 15 | yield numbers[k]; 16 | } 17 | } 18 | 19 | function* numberProcessor() { 20 | var gen = numberGenerator(); 21 | for (var x of gen) { 22 | yield x * constant; 23 | } 24 | } 25 | 26 | function* printNumbers() { 27 | var gen = numberProcessor(); 28 | for (var x of gen) { 29 | print(x); 30 | } 31 | } 32 | 33 | // Example: iterate over the generated numbers 34 | /* 35 | var gen = numberGenerator(); 36 | for (var x of gen) { 37 | console.log('generated number %d', x); 38 | } 39 | */ 40 | 41 | var gen = printNumbers(); 42 | for (var x of gen) {} 43 | -------------------------------------------------------------------------------- /src/12-streams.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var numbers = [3, 1, 7]; 4 | var constant = 2; 5 | 6 | function mul(a, b) { 7 | return a * b; 8 | } 9 | function print(n) { 10 | console.log(n); 11 | } 12 | var byConstant = mul.bind(null, constant); 13 | 14 | // stream of numbers from an Array 15 | var util = require('util'); 16 | var Readable = require('stream').Readable; 17 | util.inherits(NumberStream, Readable); 18 | 19 | function NumberStream(numbers) { 20 | Readable.call(this, { 21 | objectMode: true 22 | }); 23 | this.numbers = numbers; 24 | this.index = 0; 25 | } 26 | 27 | NumberStream.prototype._read = function _read() { 28 | // utility functions for clarity 29 | function outputNextNumber() { 30 | this.push(this.numbers[this.index++]); 31 | } 32 | 33 | function isFinished() { 34 | return this.index >= this.numbers.length; 35 | } 36 | 37 | outputNextNumber.call(this); 38 | if (isFinished.call(this)) { 39 | this.push(null); 40 | } 41 | }; 42 | 43 | // I like ending stream references with "_" 44 | var numbers_ = new NumberStream(numbers); 45 | 46 | // to just print the numbers do the following 47 | /* 48 | numbers_.on('data', print); 49 | numbers_.on('end', function () { 50 | console.log('numbers stream finished'); 51 | });*/ 52 | 53 | // number multiplier stream 54 | var Transform = require('stream').Transform; 55 | util.inherits(MultiplierStream, Transform); 56 | 57 | function MultiplierStream(constant) { 58 | Transform.call(this, { 59 | objectMode: true 60 | }); 61 | this.constant = constant; 62 | } 63 | 64 | MultiplierStream.prototype._transform = 65 | function _transform(data, encoding, callback) { 66 | this.push(data * this.constant); 67 | callback(); 68 | }; 69 | 70 | var multiplier_ = new MultiplierStream(constant); 71 | 72 | numbers_ 73 | .pipe(multiplier_) 74 | .on('data', print); 75 | // 6 76 | // 2 77 | // 14 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/13-transducers.js: -------------------------------------------------------------------------------- 1 | // Make the problem slightly longer 2 | // we need to multiply the numbers, 3 | // then add 3 to each result 4 | // then filter numbers less than 10 5 | // then convert the remaining to hex and return as single string 6 | 7 | // This way we will have more steps to play with 8 | var numbers = [3, 1, 7, 5, 10]; 9 | var constant = 2; 10 | var addConstant = 3; 11 | var sep = ''; 12 | 13 | function mul(a, b) { 14 | return a * b; 15 | } 16 | function add(a, b) { 17 | return a + b; 18 | } 19 | function gt(a, b) { 20 | return a < b; 21 | } 22 | function toHex(x) { 23 | return Number(x).toString(16).toUpperCase(); 24 | } 25 | function joinStrings(separator, a, b) { 26 | if (!a) { 27 | return b; 28 | } 29 | return a + separator + b; 30 | } 31 | function print(n) { 32 | console.log(n); 33 | } 34 | var mulBy = mul.bind(null, constant); 35 | var addConst = add.bind(null, addConstant); 36 | var gt10 = gt.bind(null, 10); 37 | var joinSep = joinStrings.bind(null, sep); 38 | 39 | // traditional approach 40 | // creates full array at each step 41 | // implements join using "Array.reduce" 42 | // starting with numbers = [3, 1, 7, 5, 10] 43 | var result = numbers.map(mulBy) // [6, 2, 14, 10, 20] 44 | .map(addConst) // [9, 5, 17, 13, 23] 45 | .filter(gt10) // [17, 13, 23] 46 | .map(toHex) // ['11', 'D', '17'] 47 | .reduce(joinSep, ''); // '11D17' 48 | console.log(result); 49 | 50 | // let us remove intermediate arrays 51 | // using https://github.com/cognitect-labs/transducers-js 52 | 53 | var T = require('transducers-js'); 54 | var result = T.into( 55 | '', 56 | T.comp( // creates single callback function 57 | T.map(mulBy), // from these steps 58 | T.map(addConst), // no intermediate arrays 59 | T.filter(gt10), 60 | T.map(toHex) 61 | ), 62 | numbers 63 | ); 64 | console.log(result); 65 | // '11D17' 66 | -------------------------------------------------------------------------------- /src/14-dependency-injection.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | 11 | var heroin = require('heroin'); 12 | var mulByB = heroin(mul, { b: constant }); 13 | // mulByB will call mul(x, constant) 14 | // because it injected constant as argument "b" 15 | 16 | numbers.map(mulByB).forEach(print); 17 | // 6 2 14 18 | -------------------------------------------------------------------------------- /src/15-immutable-using-immutable.js: -------------------------------------------------------------------------------- 1 | // instead of using default JavaScript structures, 2 | // store everything in an immutable "tree" 3 | var immutable = require('immutable'); 4 | 5 | var state = immutable.fromJS({ 6 | numbers: [3, 1, 7], 7 | constant: 2 8 | }); 9 | function mul(a, b) { 10 | return a * b; 11 | } 12 | function print(n) { 13 | console.log(n); 14 | } 15 | 16 | // define a single function works on the immutable state object 17 | // and produces another full immutable state object 18 | function multiply(currentState) { 19 | var byConstant = mul.bind(null, currentState.get('constant')); 20 | return currentState.updateIn(['numbers'], function (ns) { 21 | return ns.map(byConstant); 22 | }); 23 | } 24 | 25 | var multiplied = multiply(state); 26 | console.log('original state', state); 27 | console.log('multiplied state', multiplied); 28 | console.log('is state === multiplied state?', state === multiplied); 29 | /* 30 | original state Map { "numbers": List [ 3, 1, 7 ], "constant": 2 } 31 | multiplied state Map { "numbers": List [ 6, 2, 14 ], "constant": 2 } 32 | is state === multiplied state? false 33 | */ 34 | -------------------------------------------------------------------------------- /src/15-immutable-using-seamless.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | function mul(a, b) { 5 | return a * b; 6 | } 7 | function print(n) { 8 | console.log(n); 9 | } 10 | 11 | var byConstant = mul.bind(null, constant); 12 | 13 | var immutable = require('seamless-immutable'); 14 | immutable(numbers) 15 | .map(function (x, k, array) { 16 | array[1] = 1000000; 17 | return x; 18 | }) 19 | .map(byConstant) 20 | .forEach(print); 21 | // setting the array item to another number 22 | // had no effect in the default mode 23 | // in strict mode it would cause an exception 24 | // 6 2 14 25 | -------------------------------------------------------------------------------- /src/15-immutable-with-redux.js: -------------------------------------------------------------------------------- 1 | // this is a modification of "immutable-using-immutable.js" code 2 | // that uses Redux store and actions instead of plain callbacks 3 | // to modify a single immutable data structure 4 | // see good large example in 5 | // http://teropa.info/blog/2015/09/10/full-stack-redux-tutorial.html 6 | var immutable = require('immutable'); 7 | var redux = require('redux'); 8 | 9 | // initial state is very simple - just empty list of numbers 10 | var INITIAL_STATE = immutable.fromJS({ 11 | numbers: [] 12 | }); 13 | 14 | // individual actions on the state 15 | function setNumbers(state, numbers) { 16 | return state.set('numbers', immutable.List(numbers)); 17 | } 18 | 19 | function mul(a, b) { return a * b; } 20 | 21 | function multiply(state, constant) { 22 | var byConstant = mul.bind(null, constant); 23 | return state.updateIn(['numbers'], function (ns) { 24 | return ns.map(byConstant); 25 | }); 26 | } 27 | 28 | function print(state, printer) { 29 | printer(state.get('numbers')); 30 | return state; 31 | } 32 | 33 | 34 | // reducer function (state, action) => new state 35 | function reducer(state, action) { 36 | if (!state) { 37 | state = INITIAL_STATE; 38 | } 39 | 40 | switch (action.type) { 41 | case 'SET_NUMBERS': { 42 | return setNumbers(state, action.numbers); 43 | } 44 | case 'MULTIPLY': { 45 | return multiply(state, action.constant); 46 | } 47 | case 'PRINT': { 48 | return print(state, action.printer); 49 | } 50 | } 51 | return state; 52 | } 53 | 54 | // our program is just a plain list of actions 55 | var actions = [{ 56 | type: 'SET_NUMBERS', 57 | numbers: [3, 1, 7] 58 | }, { 59 | type: 'MULTIPLY', 60 | constant: 2 61 | }, { 62 | type: 'PRINT', 63 | printer: console.log.bind(console) 64 | }]; 65 | 66 | // we could use reducer to reduce a list of actions 67 | // (hence the name "reducer") 68 | var computedState = actions.reduce(reducer, INITIAL_STATE); 69 | // List [ 6, 2, 14 ] 70 | console.log('computed state', computedState); 71 | // computed state Map { "numbers": List [ 6, 2, 14 ] } 72 | 73 | // 74 | // OR we can use a Redux Store - a wrapper for state for the state 75 | // 76 | 77 | // redux store that knows how to use our reducer function 78 | var store = redux.createStore(reducer); 79 | 80 | // execute our actions against a store 81 | actions.forEach(store.dispatch); 82 | console.log('final state', store.getState()); 83 | /* 84 | List [ 6, 2, 14 ] 85 | final state Map { "numbers": List [ 6, 2, 14 ] } 86 | */ 87 | -------------------------------------------------------------------------------- /src/16-async-await.js: -------------------------------------------------------------------------------- 1 | var numbers = [3, 1, 7]; 2 | var constant = 2; 3 | 4 | // use an promise-returning multiply 5 | var Promise = require('bluebird'); 6 | function mul(a, b) { 7 | return Promise.resolve(a * b); 8 | } 9 | 10 | // use a promise-returning print 11 | function print(n) { 12 | console.log(n); 13 | return Promise.resolve(); 14 | } 15 | 16 | var byConstant = mul.bind(null, constant); 17 | 18 | // multiplies each number one at a time 19 | function mulAll(numbers) { 20 | return Promise.map(numbers, byConstant, { concurrency: 1 }); 21 | } 22 | 23 | // prints each number one at a time 24 | function printAll(numbers) { 25 | return Promise.map(numbers, print, { concurrency: 1 }); 26 | } 27 | 28 | // uses https://github.com/yortus/asyncawait 29 | // to execute promise-returning calls just like sync code 30 | var async = require('asyncawait/async'); 31 | var await = require('asyncawait/await'); 32 | 33 | var multiplyAndPrint = async (function () { 34 | var multiplied = await (mulAll(numbers)); 35 | console.log('multiplied all numbers'); 36 | await (printAll(multiplied)); 37 | console.log('printed all numbers'); 38 | }); 39 | 40 | multiplyAndPrint().then(function () { 41 | console.log('all done'); 42 | }); 43 | 44 | /* 45 | multiplied all numbers 46 | 6 47 | 2 48 | 14 49 | printed all numbers 50 | all done 51 | */ 52 | -------------------------------------------------------------------------------- /src/17-asm.js: -------------------------------------------------------------------------------- 1 | 'use asm'; 2 | 3 | // main point is to provide primitive type information 4 | // (unsigned bytes in this case) to the engine 5 | // by using typed arrays and '|0' operators on the individual variables 6 | 7 | // Any suggestions for improving this Asm.js example are welcome 8 | // https://github.com/bahmutov/javascript-journey/issues 9 | 10 | // numbers is an array of unsigned bytes 11 | var numbers = new Uint8Array([3, 1, 7]); 12 | // constant is an integer 13 | var constant = 2|0; 14 | 15 | // multiplication of two integers 16 | function mul(a, b) { 17 | a = a|0; 18 | b = b|0; 19 | return a * b; 20 | } 21 | function print(n) { 22 | n = n|0; 23 | console.log(n); 24 | } 25 | var byConstant = mul.bind(null, constant); 26 | 27 | function iterate(list, cb) { 28 | var n = list.length|0; 29 | var k = 0; 30 | for (; k < n; k += 1) { 31 | cb(list[k]|0); 32 | } 33 | } 34 | 35 | // while v8 in node does not offer performance improvements, 36 | // other environments, like Firefox can 37 | var start = process.hrtime(); 38 | iterate(numbers, byConstant); 39 | var elapsed = process.hrtime(start); 40 | console.log('multiplication took %d seconds %d nanoseconds', 41 | elapsed[0], elapsed[1]); 42 | 43 | -------------------------------------------------------------------------------- /src/18-maybe-monad.js: -------------------------------------------------------------------------------- 1 | // goal: multiply each __even__ number and print the result 2 | 3 | var Maybe = require('data.maybe'); 4 | 5 | var numbers = [3, 2, 6, 7]; 6 | var constant = 2; 7 | 8 | function mul(a, b) { 9 | return a * b; 10 | } 11 | function print(n) { 12 | console.log(n); 13 | } 14 | function isEven(n) { 15 | return n % 2 === 0; 16 | } 17 | 18 | var byConstant = mul.bind(null, constant); 19 | function forEach(cb, array) { 20 | for (var k = 0; k < array.length; k += 1) { 21 | cb(array[k]); 22 | } 23 | } 24 | 25 | /* 26 | imperative solution would just use the "IF/ELSE" 27 | if (isEven(x)) { 28 | print(byConstant(x)) 29 | } 30 | instead we are going to put the number in a wrapper Maybe 31 | if the number is even we are going to have wrapper Maybe.Just 32 | and the Maybe.Just().map(f) will call "f" passing the value inside 33 | 34 | On the other hand, any value placed into Maybe.Nothing 35 | is lost - Maybe.Nothing().map(f) will NOT call "f" 36 | */ 37 | const maybeEven = n => isEven(n) ? Maybe.Just(n) : Maybe.Nothing(); 38 | const process = n => maybeEven(n).map(byConstant).map(print); 39 | /* 40 | 3 ----- Nothing() 41 | 2 ----- Just(2)------- Just(4) ------ "4" (Just(4) continues) 42 | 6 ----- Just(6)------- Just(12) ----- "12" (Just(12) continues) 43 | 7 ----- Nothing() 44 | */ 45 | 46 | forEach(process, numbers); 47 | // 4 12 48 | --------------------------------------------------------------------------------