└── 2017 ├── .gitignore ├── theconf-expanding-async-in-javascript ├── .node-version ├── .babelrc ├── async_generators.js ├── package.json ├── callbacks_anonymous_functions.js ├── actors_stuck.js ├── actors_process.js ├── actors.js ├── async_await.js ├── promises.js ├── callbacks_with_named_functions.js ├── rx.js ├── csp.js ├── package-lock.json ├── slides.ipynb └── .ipynb_checkpoints │ └── slides-checkpoint.ipynb ├── neovim-from-scratch ├── neovim.png ├── nvim │ ├── UltiSnips │ │ └── javascript.snippets │ └── init.vim ├── projetinho │ ├── foo.js │ ├── idades.csv │ ├── main.js │ ├── talk.js │ └── .eslintrc.js └── walkthrough.md ├── tdc-florianopolis-clojure-like-in-vim ├── neovim.png ├── vault_boy.png ├── vim-logo.png ├── clojure-logo.png ├── calculator.tim └── timl-a-clojure-like-in-vim.md └── oo-in-js-the-good-parts ├── template ├── index.html └── style.scss └── slides.md /2017/.gitignore: -------------------------------------------------------------------------------- 1 | .tmp 2 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/.node-version: -------------------------------------------------------------------------------- 1 | 8.5.0 2 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/neovim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thalesmello/slides/HEAD/2017/neovim-from-scratch/neovim.png -------------------------------------------------------------------------------- /2017/neovim-from-scratch/nvim/UltiSnips/javascript.snippets: -------------------------------------------------------------------------------- 1 | snippet log "Console log" b 2 | console.log($1)$0 3 | endsnippet 4 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/projetinho/foo.js: -------------------------------------------------------------------------------- 1 | class Foo { 2 | greet () { 3 | console.log("Hello world") 4 | } 5 | } 6 | 7 | module.exports = Foo 8 | -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/neovim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thalesmello/slides/HEAD/2017/tdc-florianopolis-clojure-like-in-vim/neovim.png -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/vault_boy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thalesmello/slides/HEAD/2017/tdc-florianopolis-clojure-like-in-vim/vault_boy.png -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/vim-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thalesmello/slides/HEAD/2017/tdc-florianopolis-clojure-like-in-vim/vim-logo.png -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/clojure-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thalesmello/slides/HEAD/2017/tdc-florianopolis-clojure-like-in-vim/clojure-logo.png -------------------------------------------------------------------------------- /2017/neovim-from-scratch/projetinho/idades.csv: -------------------------------------------------------------------------------- 1 | Nome,Idade 2 | Oswaldo,54 3 | Luciana,18 4 | Pedrinho,6 5 | Carlos,21 6 | Derek,12 7 | Marcos,28 8 | Pedro Álvarez Cabral,40 9 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": {}, 3 | "ignore": [], 4 | "plugins": ["transform-async-generator-functions"], 5 | "presets": [ 6 | "stage-3" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/calculator.tim: -------------------------------------------------------------------------------- 1 | (ns calculator) 2 | 3 | (defn buffer-sum [] 4 | (->> (#*getline "1" "$") 5 | (join " ") 6 | (#(split % " ")) 7 | (map str2nr) 8 | (apply +) 9 | (execute "echo"))) 10 | 11 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/projetinho/main.js: -------------------------------------------------------------------------------- 1 | const Foo = require('./foo') 2 | 3 | console.log('Bem vindo ao meu tutorial!') 4 | console.log('Vocês vão gostar!') 5 | 6 | function main() { 7 | const myFoo = new Foo(); 8 | myFoo.greet(); 9 | console.log("Hi"); 10 | } 11 | 12 | main(); 13 | 14 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/projetinho/talk.js: -------------------------------------------------------------------------------- 1 | function smalltalk() { 2 | console.log("Hello Claire! How are you?") 3 | console.log("I heard business is doing well, Claire") 4 | console.log("Thanks, Claire, my Vimfile is just fine!") 5 | console.log("Talk to you in the next one, Claire!") 6 | } 7 | 8 | let name = 'Developer' 9 | console.log(`Hello ${name}`) 10 | 11 | module.exports = { smalltalk } 12 | 13 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/async_generators.js: -------------------------------------------------------------------------------- 1 | const { delay } = require('bluebird') 2 | async function * producer() { 3 | const stream = [1, 2, 3, 4, 5] 4 | for (let val of stream) { 5 | await delay(1000) 6 | yield val 7 | } 8 | } 9 | 10 | async function consumer () { 11 | for await (let val of producer()) { 12 | console.log(val) 13 | } 14 | } 15 | 16 | consumer() 17 | -------------------------------------------------------------------------------- /2017/oo-in-js-the-good-parts/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{title}} 6 | {{{style}}} 7 | 8 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/projetinho/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "rules": { 8 | "indent": [ 9 | "error", 10 | 2 11 | ], 12 | "linebreak-style": [ 13 | "error", 14 | "unix" 15 | ], 16 | "quotes": [ 17 | "error", 18 | "single" 19 | ], 20 | "semi": [ 21 | "error", 22 | "never" 23 | ] 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theconf-expanding-async-in-javascript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "babel-plugin-transform-async-generator-functions": "^6.24.1", 13 | "bluebird": "^3.5.0", 14 | "comedy": "^1.2.0", 15 | "js-csp": "^1.0.1", 16 | "rxjs": "^5.4.3", 17 | "top-level-await": "^1.1.0" 18 | }, 19 | "devDependencies": { 20 | "babel-plugin-syntax-async-generators": "^6.13.0", 21 | "babel-preset-stage-3": "^6.24.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/nvim/init.vim: -------------------------------------------------------------------------------- 1 | call plug#begin() 2 | Plug 'morhetz/gruvbox' 3 | Plug 'terryma/vim-multiple-cursors' 4 | Plug 'sheerun/vim-polyglot' 5 | Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } 6 | Plug 'junegunn/fzf.vim' 7 | Plug 'roxma/nvim-completion-manager' 8 | Plug 'w0rp/ale' 9 | Plug 'cohama/lexima.vim' 10 | Plug 'SirVer/ultisnips' 11 | Plug 'honza/vim-snippets' 12 | call plug#end() 13 | 14 | colorscheme gruvbox 15 | set background=dark 16 | 17 | set hidden 18 | 19 | set number 20 | set relativenumber 21 | set termguicolors 22 | 23 | set mouse=a 24 | 25 | set inccommand=split 26 | set clipboard=unnamed 27 | 28 | set expandtab 29 | set shiftwidth=2 30 | 31 | let mapleader="\" 32 | 33 | nnoremap ; A; 34 | nnoremap sv :source $MYVIMRC 35 | nnoremap :Files 36 | nnoremap :Ag 37 | 38 | let g:UltiSnipsEditSplit="vertical" 39 | let g:UltiSnipsSnippetsDir = '~/.config/nvim/UltiSnips' 40 | 41 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/callbacks_anonymous_functions.js: -------------------------------------------------------------------------------- 1 | var timeout = (time, fn) => setTimeout(fn, time) 2 | var progress = fn => { 3 | var handle = setInterval(() => process.stdout.write('.'), 50) 4 | fn(() => clearTimeout(handle)) 5 | } 6 | 7 | progress(done => { 8 | var aBootTime = 1000 9 | var bBootTime = 2000 10 | var queueCallback = null 11 | var serverHandler = null 12 | console.log("A: Booting up system...") 13 | timeout(aBootTime, () => { 14 | console.log("A: Checking network connection") 15 | timeout(500, () => { 16 | console.log("A: Request complex computation") 17 | sendRequest(value => { 18 | console.log("A: Computation returned " + value) 19 | done() 20 | }) 21 | }) 22 | }) 23 | 24 | console.log("B: Booting up system...") 25 | timeout(bBootTime, () => { 26 | console.log("B: Server up and running") 27 | serverHandler = (callback) => { 28 | console.log("B: Starting heavy computation") 29 | timeout(2000, () => callback(42)) 30 | } 31 | if (queueCallback) { 32 | serverHandler(queueCallback) 33 | queueCallback = null 34 | } 35 | }) 36 | 37 | function sendRequest(callback) { 38 | if (serverHandler) { serverHandler(callback) } 39 | else { queueCallback = callback } 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/actors_stuck.js: -------------------------------------------------------------------------------- 1 | var actors = require('comedy'); 2 | var asyncProgress = fn => { 3 | var handle = setInterval(() => process.stdout.write('.'), 50) 4 | return new Promise((resolve, reject) => { 5 | fn().then(value => { 6 | clearTimeout(handle) 7 | resolve(value) 8 | }).catch(error => { 9 | clearTimeout(handle) 10 | reject(error) 11 | }) 12 | }) 13 | } 14 | 15 | class StuckActor { 16 | async sayHello(to) { 17 | var sleep = time => { 18 | let start = new Date().getTime() 19 | let expire = start + time; 20 | 21 | while (new Date().getTime() < expire); 22 | } 23 | 24 | await sleep(1000) 25 | 26 | return `Hello to ${to} from StuckActor`; 27 | } 28 | } 29 | 30 | var actorSystem = actors(); 31 | 32 | asyncProgress(() => { 33 | return actorSystem 34 | .rootActor() 35 | .then(rootActor => rootActor.createChild(StuckActor, { mode: 'forked' })) 36 | 37 | .then(myActor => { 38 | console.log('Going to send my actor a message') 39 | return myActor.sendAndReceive('sayHello', 'World') 40 | }) 41 | .then(reply => { 42 | console.log(`ProcessAct replied: ${reply}`); 43 | }) 44 | .finally(() => actorSystem.destroy()); 45 | }) 46 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/actors_process.js: -------------------------------------------------------------------------------- 1 | var actors = require('comedy'); 2 | var asyncProgress = fn => { 3 | var handle = setInterval(() => process.stdout.write('.'), 50) 4 | return new Promise((resolve, reject) => { 5 | fn().then(value => { 6 | clearTimeout(handle) 7 | resolve(value) 8 | }).catch(error => { 9 | clearTimeout(handle) 10 | reject(error) 11 | }) 12 | }) 13 | } 14 | 15 | class ProcessActor { 16 | async sayHello(to) { 17 | var sleep = time => { 18 | let start = new Date().getTime() 19 | let expire = start + time; 20 | 21 | while (new Date().getTime() < expire); 22 | } 23 | 24 | await sleep(1000) 25 | 26 | return `Hello to ${to} from ProcessActor`; 27 | } 28 | } 29 | 30 | var actorSystem = actors(); 31 | 32 | asyncProgress(() => { 33 | return actorSystem 34 | .rootActor() 35 | .then(rootActor => rootActor.createChild(ProcessActor, { mode: 'forked' })) 36 | 37 | .then(myActor => { 38 | console.log('Going to send my actor a message') 39 | return myActor.sendAndReceive('sayHello', 'World') 40 | }) 41 | .then(reply => { 42 | console.log(`ProcessAct replied: ${reply}`); 43 | }) 44 | .finally(() => actorSystem.destroy()); 45 | }) 46 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/actors.js: -------------------------------------------------------------------------------- 1 | var actors = require('comedy'); 2 | var asyncProgress = fn => { 3 | var handle = setInterval(() => process.stdout.write('.'), 50) 4 | return new Promise((resolve, reject) => { 5 | fn().then(value => { 6 | clearTimeout(handle) 7 | resolve(value) 8 | }).catch(error => { 9 | clearTimeout(handle) 10 | reject(error) 11 | }) 12 | }) 13 | } 14 | 15 | class Actor { 16 | async sayHello(to) { 17 | var { delay } = require('bluebird') 18 | 19 | await delay(1000) 20 | 21 | return `Hello to ${to} from Actor`; 22 | } 23 | } 24 | 25 | // Create an actor system. 26 | var actorSystem = actors(); 27 | 28 | asyncProgress(() => { 29 | return actorSystem 30 | // Get a root actor reference. 31 | .rootActor() 32 | // Create a class-defined child actor, that is run in a separate process (forked mode). 33 | .then(rootActor => rootActor.createChild(Actor)) 34 | // Send a message to our forked actor with a self process PID. 35 | 36 | .then(myActor => { 37 | console.log('Going to send my actor a message') 38 | return myActor.sendAndReceive('sayHello', 'World') 39 | }) 40 | .then(reply => { 41 | // Output result. 42 | console.log(`Actor replied: ${reply}`); 43 | }) 44 | // Destroy the system, killing all actor processes. 45 | .finally(() => actorSystem.destroy()); 46 | }) 47 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/async_await.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird') 2 | var { delay, all } = Promise 3 | var asyncProgress = fn => { 4 | var handle = setInterval(() => process.stdout.write('.'), 50) 5 | return new Promise((resolve, reject) => { 6 | fn().then(value => { 7 | clearTimeout(handle) 8 | resolve(value) 9 | }).catch(error => { 10 | clearTimeout(handle) 11 | reject(error) 12 | }) 13 | }) 14 | } 15 | 16 | asyncProgress(main) 17 | 18 | async function main () { 19 | var [serverA, serverB] = await all([getServerA(), getServerB()]) 20 | await serverA.sendRequest(serverB) 21 | } 22 | 23 | async function getServerA(bootTime) { 24 | console.log("A: Booting up system...") 25 | await delay(bootTime) 26 | await checkNetwork() 27 | 28 | return { sendRequest } 29 | 30 | function checkNetwork() { 31 | console.log("A: Checking network connection") 32 | return delay(500) 33 | } 34 | 35 | async function sendRequest(remoteServer) { 36 | console.log("A: Request complex computation") 37 | var value = await remoteServer.compute(2) 38 | console.log(`A: Computation returned ${value}`) 39 | } 40 | } 41 | 42 | async function getServerB(bootTime) { 43 | console.log("B: Booting up system...") 44 | await delay(bootTime) 45 | console.log("B: Server up and running") 46 | return { compute } 47 | 48 | async function compute(value) { 49 | console.log("B: Starting heavy computation") 50 | await delay(2000) 51 | return 40 + value 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/promises.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird') 2 | var { delay } = Promise 3 | var asyncProgress = fn => { 4 | var handle = setInterval(() => process.stdout.write('.'), 50) 5 | return new Promise((resolve, reject) => { 6 | fn().then(value => { 7 | clearTimeout(handle) 8 | resolve(value) 9 | }).catch(error => { 10 | clearTimeout(handle) 11 | reject(error) 12 | }) 13 | }) 14 | } 15 | 16 | asyncProgress(main) 17 | 18 | function main () { 19 | return Promise.props({ 20 | serverA: getServerA(1000), 21 | serverB: getServerB(100) 22 | }).then(({ serverA, serverB }) => { 23 | return serverA.sendRequest(serverB) 24 | }) 25 | } 26 | 27 | function getServerA(bootTime) { 28 | console.log("A: Booting up system...") 29 | 30 | return Promise.delay(bootTime) 31 | .then(checkNetwork) 32 | .then(() => ({ sendRequest })) 33 | 34 | function checkNetwork() { 35 | console.log("A: Checking network connection") 36 | return delay(500) 37 | } 38 | 39 | function sendRequest(remoteServer) { 40 | console.log("A: Request complex computation") 41 | return remoteServer 42 | .compute(2) 43 | .then(value => { 44 | console.log 45 | console.log(`A: Computation returned ${value}`) 46 | }) 47 | } 48 | } 49 | 50 | function getServerB(bootTime) { 51 | console.log("B: Booting up system...") 52 | 53 | return Promise.delay(bootTime).then(() => { 54 | console.log("B: Server up and running") 55 | return { compute } 56 | }) 57 | 58 | function compute(value) { 59 | console.log("B: Starting heavy computation") 60 | return delay(2000).then(() => 40 + value) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/callbacks_with_named_functions.js: -------------------------------------------------------------------------------- 1 | var timeout = (time, fn) => setTimeout(fn, time) 2 | var progress = fn => { 3 | var handle = setInterval(() => process.stdout.write('.'), 50) 4 | fn(() => clearTimeout(handle)) 5 | } 6 | 7 | var aBootTime = 1000 8 | var bBootTime = 1000 9 | var queueCallback = null 10 | var serverHandler = null 11 | 12 | progress((done) => { 13 | serverA(done) 14 | serverB() 15 | }) 16 | 17 | function serverA(done) { 18 | console.log("A: Booting up system...") 19 | timeout(1000, checkNetwork) 20 | 21 | function checkNetwork() { 22 | console.log("A: Checking network connection") 23 | timeout(500, sendRequest) 24 | } 25 | 26 | function sendRequest() { 27 | console.log("A: Request complex computation") 28 | sendNetworkRequest(callback) 29 | } 30 | 31 | function callback(value) { 32 | console.log("A: Computation returned " + value) 33 | done() 34 | } 35 | } 36 | 37 | function serverB() { 38 | console.log("B: Booting up system...") 39 | timeout(1000, listenRequests) 40 | 41 | function listenRequests() { 42 | console.log("B: Server up and running") 43 | serverHandler = handler 44 | 45 | if (queueCallback) { 46 | serverHandler(queueCallback) 47 | queueCallback = null 48 | } 49 | } 50 | 51 | function handler(callback) { 52 | console.log("B: Starting heavy computation") 53 | timeout(2000, () => callback(42)) 54 | } 55 | } 56 | 57 | function sendNetworkRequest(callback) { 58 | if(serverHandler) { 59 | serverHandler(callback) 60 | } else { 61 | queueCallback = callback 62 | } 63 | } 64 | // A: Booting up system... 65 | // B: Booting up system... 66 | // A: Checking network connection 67 | // B: Server up and running 68 | // A: Request complex computation 69 | // B: Starting heavy computation 70 | // A: Computation returned 42 71 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/rx.js: -------------------------------------------------------------------------------- 1 | var Promise = require('bluebird') 2 | var { delay } = Promise 3 | var { Observable } = require('rxjs') 4 | var asyncProgress = fn => { 5 | var handle = setInterval(() => process.stdout.write('.'), 50) 6 | return new Promise((resolve, reject) => { 7 | fn().then(value => { 8 | clearTimeout(handle) 9 | resolve(value) 10 | }).catch(error => { 11 | clearTimeout(handle) 12 | reject(error) 13 | }) 14 | }) 15 | } 16 | 17 | asyncProgress(main) 18 | 19 | function main () { 20 | return Promise.props({ 21 | serverA: getServerA(1000), 22 | serverB: getServerB(100) 23 | }).then(({ serverA, serverB }) => { 24 | return serverA.sendRequest(serverB) 25 | }) 26 | } 27 | 28 | function getServerA(bootTime) { 29 | console.log("A: Booting up system...") 30 | 31 | return Promise.delay(bootTime) 32 | .then(checkNetwork) 33 | .then(() => ({ sendRequest })) 34 | 35 | function checkNetwork() { 36 | console.log("A: Checking network connection") 37 | return delay(500) 38 | } 39 | 40 | function sendRequest(remoteServer) { 41 | console.log("A: Request complex computation") 42 | 43 | return remoteServer.compute(2) 44 | .do(value => console.log(`A: Received value ${value}`)) 45 | .take(10) 46 | .reduce((acc, value) => acc + value, 0) 47 | .toPromise() 48 | .then(total => console.log(`A: Computation returned ${total}`)) 49 | } 50 | } 51 | 52 | function getServerB(bootTime) { 53 | console.log("B: Booting up system...") 54 | 55 | return Promise.delay(bootTime).then(() => { 56 | console.log("B: Server up and running") 57 | return { compute } 58 | }) 59 | 60 | function compute(value) { 61 | console.log("B: Starting heavy computation") 62 | return Observable.interval(200) 63 | .scan((acc, x, i) => acc + i * 2, value) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /2017/tdc-florianopolis-clojure-like-in-vim/timl-a-clojure-like-in-vim.md: -------------------------------------------------------------------------------- 1 | # TimL: um Lisp à la Clojure no seu Vim 2 | 3 | ## By thalesmello 4 | 5 | ## Talks available at github.com/thalesmello/slides 6 | 7 | --- 8 | 9 | Boa tarde! 10 | 11 | --- 12 | 13 | Vamos falar de Vim 14 | 15 | ![](./vim-logo.png) 16 | 17 | --- 18 | 19 | E um pouco de Clojure (indiretamente) 20 | 21 | ![](./clojure-logo.png) 22 | 23 | --- 24 | 25 | Clojure é um LISP com algumas características interessantes 26 | 27 | - Compilada para Java Bytecode 28 | - Interop com Java 29 | - Tipos persistentes 30 | - Concorrência 31 | 32 | --- 33 | 34 | TimL é um LISP inspirado em Clojure 35 | https://github.com/tpope/timl 36 | 37 | - Compila para VimScript 38 | - Interop com Vim 39 | - Tipos persistentes 40 | - Single threaded (aí já seria pedir demais, né?) 41 | 42 | Criada pelo Tim Pope, pai dos plugins do Vim. 43 | 44 | --- 45 | 46 | Mas podemos usá-la pra fazer plugins! 47 | 48 | ![](./vault_boy.png) 49 | 50 | --- 51 | 52 | Instale o plugin com seu Package Manager favorito ;) 53 | 54 | Recomendação: *vim-plug* 55 | https://github.com/junegunn/vim-plug 56 | 57 | ```vim 58 | Plug 'tpope/timl' 59 | ``` 60 | 61 | --- 62 | 63 | Tome cuidado com o bug #12 64 | https://github.com/tpope/timl/issues/12 65 | 66 | ```vim 67 | let do_symbol = timl#symbol#intern('do') 68 | let number_one = timl#reader#read_string_all('1') 69 | let code = timl#cons#create(do_symbol, number_one) 70 | call timl#loader#eval(code) 71 | ``` 72 | 73 | --- 74 | 75 | Como funciona? 76 | 77 | - Core da linguagem foi escrito em Vimscript 78 | - Extensões foram escritas em TimL (como Clojure) 79 | - Arquivos gerados são cacheados 80 | 81 | --- 82 | 83 | Vou usar pra programar meu vim! 84 | 85 | ** Não faça isso! ** 86 | 87 | --- 88 | 89 | Este é apenas um experimento, pra ver até onde seria 90 | possível chegar com Vimscript. 91 | 92 | --- 93 | 94 | Existem formas melhores de se colocar escrever 95 | plugins em Clojure... ou Haskell... ou na sua 96 | linguagem de preferência. 97 | 98 | Usando `remote plugins` do Neovim. 99 | 100 | - Clojure: https://github.com/clojure-vim/neovim-client 101 | - Haskell: https://github.com/neovimhaskell/nvim-hs 102 | - Outros: https://github.com/neovim/neovim/wiki/Related-projects 103 | 104 | ![](./neovim.png) 105 | 106 | --- 107 | 108 | Obrigado! 109 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/csp.js: -------------------------------------------------------------------------------- 1 | var csp = require('js-csp') 2 | var Promise = require('bluebird') 3 | var { delay, all } = Promise 4 | var asyncProgress = fn => { 5 | var handle = setInterval(() => process.stdout.write('.'), 50) 6 | return new Promise((resolve, reject) => { 7 | fn().then(value => { 8 | clearTimeout(handle) 9 | resolve(value) 10 | }).catch(error => { 11 | clearTimeout(handle) 12 | reject(error) 13 | }) 14 | }) 15 | } 16 | var toPromise = channel => { 17 | return new Promise(resolve => 18 | csp.go(function * () { 19 | resolve(yield csp.take(channel)) 20 | }) 21 | ) 22 | } 23 | 24 | asyncProgress(main) 25 | 26 | async function main () { 27 | var [serverA, serverB] = await all([getServerA(), getServerB()]) 28 | await serverA.sendRequest(serverB) 29 | } 30 | 31 | async function getServerA(bootTime) { 32 | console.log("A: Booting up system...") 33 | await delay(bootTime) 34 | await checkNetwork() 35 | 36 | return { sendRequest } 37 | 38 | function checkNetwork() { 39 | console.log("A: Checking network connection") 40 | return delay(500) 41 | } 42 | 43 | async function sendRequest(remoteServer) { 44 | console.log("A: Request complex computation") 45 | var channel = remoteServer.compute(2) 46 | 47 | var value = await toPromise(csp.go(function * () { 48 | var acc = 0 49 | var count = 0 50 | while (true) { 51 | var value = yield csp.take(channel) 52 | 53 | console.log(`A: Received ${value}`) 54 | 55 | acc += value 56 | count += 1 57 | 58 | if (count > 10) { 59 | channel.close() 60 | return acc 61 | } 62 | } 63 | })) 64 | 65 | console.log(`A: Computation returned ${value}`) 66 | } 67 | } 68 | 69 | async function getServerB(bootTime) { 70 | console.log("B: Booting up system...") 71 | await delay(bootTime) 72 | console.log("B: Server up and running") 73 | return { compute } 74 | 75 | function compute(value) { 76 | console.log("B: Starting heavy computation") 77 | 78 | var channel = csp.chan() 79 | 80 | csp.go(function * () { 81 | var current = value 82 | var i = 1 83 | 84 | while (yield csp.put(channel, current)) { 85 | current += i * 2 86 | i++ 87 | 88 | yield csp.timeout(200) 89 | } 90 | }) 91 | 92 | return channel 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /2017/neovim-from-scratch/walkthrough.md: -------------------------------------------------------------------------------- 1 | Como configurar o seu NeoVim do Zero 2 | 3 | --- 4 | 5 | Então você está começando a usar Vim? 6 | 7 | Você sabe o que todas as linhas no seu Vimfile significam? 8 | 9 | --- 10 | 11 | São tantas opções de configuração, que isso acaba afugentando novatos 12 | 13 | E foi isso que me afastou do Vim durante tanto tempo 14 | 15 | --- 16 | 17 | Depois de ter me baseado no Vimfile de alguns amigos, eu personalizei o meu próprio. 18 | 19 | E isso me possibilita ser bastante produtivo ao editar texto hoje em dia. 20 | 21 | --- 22 | 23 | Entretanto, esse processo pode ser bastante trabalhoso para um iniciante. 24 | 25 | Meu objetivo com esta talk é mostrar apenas *um* caminho das pedras que você 26 | pode seguir para configurar o seu Neovim de uma maneira que seja mais útil 27 | do que as configurações padrão. 28 | 29 | --- 30 | 31 | A propósito, você já ouviu falar do Neovim? 32 | 33 | ![](./neovim.png) 34 | 35 | --- 36 | 37 | Para quem não conhece, o Neovim é um fork do Vim, só que com uma participação mais 38 | ativa da comunidade. Ele acaba tendo algumas features adicionais que tornam ele 39 | mais atraente do que o Vim padrão. 40 | 41 | Por conta disso, vamos utilizar o Neovim nesta talk. 42 | 43 | --- 44 | 45 | Neovim 46 | https://github.com/neovim/neovim 47 | 48 | --- 49 | 50 | Roteiro 51 | 52 | - Configurações básicas e úteis, explicando o que cada uma faz 53 | - Como criar atalhos customizados com a tecla "leader" 54 | - Substituição de texto com preview em tempo real 55 | - Gerenciamento de plugin básico com o "vim-plug" 56 | - Deixar o seu NeoVim bonitão com o tema "gruvbox" 57 | - Editando texto com múltiplos cursores estilo "Sublime" 58 | - Navegação de arquivos "fuzzy" estilo "Sublime Ctrl-P" com "fzf-vim" 59 | - Auto completar código com o "nvim-completion-manager" 60 | - Linting da sua linguagem favorita com o "ale" 61 | - Auto completar pares "()", "{} e "[]" com o "auto-pairs" 62 | - Expansão de snippets com o "UltiSnips" 63 | - Como usar comandos "bash" para manipular seu código 64 | 65 | --- 66 | 67 | Mão no código! 68 | 69 | --- 70 | 71 | Gerenciador de pacotes `vim-plug` 72 | 73 | https://github.com/junegunn/vim-plug 74 | 75 | --- 76 | 77 | `gruvbox` 78 | https://github.com/morhetz/gruvbox 79 | 80 | --- 81 | 82 | `vim-multiple-cursors` 83 | 84 | https://github.com/terryma/vim-multiple-cursors 85 | 86 | --- 87 | 88 | `vim-polyglot` 89 | 90 | https://github.com/sheerun/vim-polyglot 91 | 92 | --- 93 | 94 | `vim-fzf` 95 | 96 | https://github.com/junegunn/fzf.vim 97 | 98 | ``` 99 | Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } 100 | Plug 'junegunn/fzf.vim' 101 | ``` 102 | 103 | --- 104 | 105 | `nvim-completion-manager` 106 | 107 | https://github.com/roxma/nvim-completion-manager 108 | 109 | --- 110 | 111 | `ale` 112 | https://github.com/w0rp/ale 113 | 114 | --- 115 | 116 | `auto-pairs` 117 | https://github.com/jiangmiao/auto-pairs 118 | 119 | --- 120 | 121 | `ultisnips` 122 | https://github.com/SirVer/ultisnips 123 | 124 | ``` 125 | Plugin 'SirVer/ultisnips' 126 | Plugin 'honza/vim-snippets'" 127 | 128 | let g:UltiSnipsEditSplit="vertical" 129 | let g:UltiSnipsSnippetsDir = '~/.config/nvim/UltiSnips' 130 | ``` 131 | 132 | --- 133 | 134 | Orientações finais 135 | 136 | - Configure o Neovim pra ficar do seu jeito 137 | - Leia a documentação 138 | 139 | --- 140 | 141 | Talk disponível em: 142 | https://github.com/thalesmello/slides/tree/master/2017/neovim-from-scratch 143 | 144 | 145 | -------------------------------------------------------------------------------- /2017/oo-in-js-the-good-parts/template/style.scss: -------------------------------------------------------------------------------- 1 | // Theme 2 | // --------------------------------------------------------------------------- 3 | 4 | $primary : #3F51B5; 5 | $secondary : #009688; 6 | $tertiary : #757575; 7 | $light : #FFF; 8 | $dark : #333; 9 | $text-dark : #212121; 10 | $text-light : $light; 11 | $code-background : #F8F8F8; 12 | $overlay : transparentize(#000, .5); 13 | $font-size : 33px; 14 | $font-size-impact : 128px; 15 | $font : Arial, Helvetica, sans-serif; 16 | $font-title : Arial, Helvetica, sans-serif; 17 | $font-fixed : 'Lucida Console', Monaco, monospace; 18 | $margin : 20px; 19 | $iframe-scale : 1.5; 20 | 21 | 22 | // CSS Base 23 | // --------------------------------------------------------------------------- 24 | 25 | * { box-sizing: border-box; } 26 | body { font-family: $font; } 27 | h1, h2, h3, h4, h5, h6 { 28 | margin: 0 0 $margin 0; 29 | font-family: $font-title; 30 | } 31 | h1 { color: $primary; } 32 | h2 { color: $secondary; } 33 | h3 { color: $tertiary; } 34 | li { margin-bottom: .25em; }; 35 | pre, code { 36 | text-align: left; 37 | font-family: $font-fixed; 38 | color: $secondary; 39 | background: $code-background; 40 | } 41 | a, a:visited, a:hover, a:active { color: $text-dark; } 42 | img { vertical-align: inherit; } 43 | blockquote { 44 | border-left: 8px solid; 45 | padding-left: .5em; 46 | color: $tertiary; 47 | text-align: left; 48 | margin: 1em 0; 49 | & > p { margin: 0; } 50 | } 51 | 52 | 53 | // Remark base 54 | // --------------------------------------------------------------------------- 55 | 56 | .remark-code { font-size: .5em; } 57 | .remark-container { background: $dark; } 58 | .remark-slide-scaler { box-shadow: none; } 59 | .remark-notes { font-size: 1.5em; } 60 | 61 | .remark-slide-content { 62 | font-size: $font-size; 63 | padding: 1em 2em; 64 | color: $text-dark; 65 | background-size: cover; 66 | } 67 | 68 | .remark-slide-number { 69 | color: $text-light; 70 | right: 1em; 71 | opacity: .6; 72 | font-size: 0.8em; 73 | z-index: 2; 74 | .no-counter & { display: none; } 75 | } 76 | 77 | // Additions 78 | .impact { 79 | background-color: $primary; 80 | vertical-align: middle; 81 | text-align: center; 82 | &, h1, h2 { color: $text-light; } 83 | h1 { font-size: $font-size-impact; } 84 | } 85 | 86 | .full { 87 | &, h1, h2 { color: $text-light; } 88 | &iframe { 89 | height: calc(#{100%/$iframe-scale} - 1.2em); 90 | width: 100%/$iframe-scale; 91 | transform: scale($iframe-scale); 92 | transform-origin: 0 0; 93 | border: 0; 94 | } 95 | } 96 | 97 | .bottom-bar { 98 | background-color: $primary; 99 | color: $text-light; 100 | position: absolute; 101 | bottom: 0; 102 | left: 0; 103 | right: 0; 104 | font-size: 20px; 105 | padding: .8em; 106 | text-align: left; 107 | z-index: 1; 108 | p { margin: 0;} 109 | .impact &, .full & { display: none; } 110 | } 111 | 112 | 113 | // Utilities 114 | // --------------------------------------------------------------------------- 115 | 116 | // Positioning 117 | .side-layer { 118 | position: absolute; 119 | left: 0; 120 | width: 100%; 121 | padding: 0 2em; 122 | } 123 | .middle { &, & img, & span { vertical-align: middle; } }; 124 | .top { vertical-align: top; }; 125 | .bottom { vertical-align: bottom; }; 126 | .inline-block { 127 | p, ul, ol, blockquote { 128 | display: inline-block; 129 | text-align: left; 130 | } 131 | } 132 | .no-margin { &, & > p, & > pre, & > ul, & > ol { margin: 0; } } 133 | .no-padding { padding: 0; } 134 | .space-left { padding-left: 1em; } 135 | .space-right { padding-right: 1em; } 136 | 137 | // Images 138 | .responsive > img { width: 100%; height: auto; }; 139 | .contain { background-size: contain; }; 140 | .overlay { box-shadow: inset 0 0 0 9999px $overlay; } 141 | 142 | // Text 143 | .left { text-align: left; } 144 | .right { text-align: right; } 145 | .center { text-align: center; } 146 | .justify { text-align: justify; } 147 | .primary { color: $primary; } 148 | .alt { color: $secondary; }; 149 | .em { color: $tertiary; }; 150 | .thin { font-weight: 200; } 151 | .huge { font-size: 2em; } 152 | .big { font-size: 1.5em; } 153 | .small { font-size: .8em; } 154 | .dark-bg { background-color: $dark; } 155 | .alt-bg { background-color: $secondary; }; 156 | 157 | // Simple 12-columns grid system 158 | .row { 159 | width: 100%; 160 | &::after { 161 | content: ''; 162 | display: table; 163 | clear: both; 164 | } 165 | &.table { display: table; }; 166 | &.table [class^="col-"] { 167 | float: none; 168 | display: table-cell; 169 | vertical-align: inherit; 170 | } 171 | } 172 | 173 | [class^="col-"] { 174 | float: left; 175 | &.inline-block { 176 | float: none; 177 | display: inline-block; 178 | } 179 | } 180 | 181 | @for $i from 1 through 12 { 182 | .col-#{$i} {width: 100% / 12 * $i; } 183 | } 184 | 185 | // Animations 186 | @keyframes fadeIn { 187 | from { opacity: 0; } 188 | to { opacity: 1; } 189 | } 190 | 191 | .animation-fade { 192 | animation-duration: 300ms; 193 | animation-fill-mode: both; 194 | animation-timing-function: ease-out; 195 | .remark-visible & { animation-name: fadeIn; } 196 | } 197 | 198 | 199 | // Fix PDF print with chrome 200 | // --------------------------------------------------------------------------- 201 | 202 | @page { 203 | // 908px 681px for 4/3 slides 204 | size: 1210px 681px; 205 | margin: 0; 206 | } 207 | 208 | @media print { 209 | .remark-slide-scaler { 210 | width: 100% !important; 211 | height: 100% !important; 212 | transform: scale(1) !important; 213 | top: 0 !important; 214 | left: 0 !important; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /2017/oo-in-js-the-good-parts/slides.md: -------------------------------------------------------------------------------- 1 | title: My Presentation 2 | class: animation-fade no-counter 3 | layout: true 4 | 5 | --- 6 | 7 | class: impact 8 | 9 | # OO in JavaScript 10 | # The Good Parts 11 | 12 | Thales Mello https://github.com/thalesmello 13 | 14 | Derek Stavis https://github.com/derekstavis 15 | 16 | TDC São Paulo 17 | 18 | --- 19 | 20 | # Por que você programa JavaScript? 21 | 22 | -- 23 | 24 | Pode ser que seja pra criar aplicações web 25 | 26 | -- 27 | 28 | Pode ser que seja pra criar servidores super performáticos 29 | 30 | -- 31 | 32 | Pode ser que seja para reaproveitar o mesmo código no browser e no servidor 😉 33 | 34 | -- 35 | 36 | Mas ainda assim, JavaScript é uma linguagem com um histórico bastante confuso 37 | 38 | -- 39 | 40 | É uma linguagem funcional? É uma linguagem imperativa? 41 | 42 | 43 | --- 44 | 45 | # É uma mistura dos dois 46 | 47 | Predominantemente, é uma linguagem .alt[imperativa] mas com características .alt[funcionais] e de .alt[orientação a objetos] 48 | 49 | -- 50 | 51 | A comunidade tem seguido muito por uma linha funcional nos últimos anos, deixando a capacidade de trabalhar com objetos um pouco de lado 52 | 53 | -- 54 | 55 | ## O ponto é 56 | 57 | Frequentemente lidamos com problemas de arquitetura que são muito elegantemente resolvidos utilizando .alt[padrões de design OO] 58 | 59 | --- 60 | 61 | class: impact 62 | 63 | # É sobre isso que vamos falar hoje 64 | 65 | --- 66 | 67 | # Por que diabos Orientação a Objetos é Relevante? 68 | 69 | Afinal, programação funcional não é uma maneira muito melhor de se escrever código? 70 | 71 | -- 72 | 73 | Isso não é *sempre* verdade 74 | 75 | -- 76 | 77 | Por exemplo, é muito comum precisar trabalhar sempre com os mesmos conjunto de dados e funções 78 | 79 | -- 80 | 81 | Numa situação dessas, faria muito sentido atrelar aquele conjunto de dados àquelas funções num único objeto 82 | 83 | -- 84 | 85 | É disso que se trata orientação a objetos 86 | 87 | --- 88 | 89 | Antes de a gente continuar, o primeiro passo para manter a sanidade mental ao se trabalhar com orientação a objetos é adotar 90 | 91 | -- 92 | 93 | # Imutabilidade 94 | 95 | -- 96 | 97 | Afinal, não é nenhum conceito exclusivo de linguagens funcionais. 98 | 99 | -- 100 | 101 | Para trabalhar adotar imutabilidade, basta trabalhar com o conceito de que os métodos (ou funções) de um objeto sempre vão retornar .alt[novos objetos, sem mudar o estado] 102 | 103 | -- 104 | 105 | --- 106 | 107 | 108 | ```javascript 109 | function main () { 110 | const user = { 111 | name: 'Filipe', 112 | surname: 'Devlin' 113 | } 114 | 115 | const getUserFullName = user => `${user.name} ${user.surname}` 116 | const modifyUser = (user, newProps) => Object.assign({}, user, newProps) 117 | 118 | console.log(getUserFullName(modifyUser(user, { name: 'Mestre' }))) 119 | } 120 | ``` 121 | 122 | --- 123 | 124 | 125 | ```javascript 126 | function main () { 127 | const user = new User({ 128 | name: 'Filipe', 129 | surname: 'Devlin' 130 | }) 131 | 132 | console.log(user.modify({ name: 'Mestre' }).fullName()) 133 | } 134 | 135 | class User { 136 | constructor ({ name, surname }) { 137 | this.name = name 138 | this.surname = surname 139 | } 140 | 141 | fullName () { 142 | return `${this.name} ${this.surname}` 143 | } 144 | 145 | modify (newProps) { 146 | return new User(Object.assign({}, this, newProps)) 147 | } 148 | } 149 | ``` 150 | 151 | --- 152 | 153 | Sabe quando você tem um monte de `if (obj == null)` em várias partes do código? 154 | 155 | -- 156 | 157 | # Null Object Pattern 158 | 159 | Em vez de fazer isso, utilize um Null Object, que obedece à mesma interface do seu objeto original. 160 | 161 | --- 162 | 163 | 164 | ```javascript 165 | document.onload = onPageLoad 166 | 167 | function onPageLoad () { 168 | Promise.resolve(localStorage) 169 | .then(({ token }) => { 170 | if (token) { 171 | return fetch('https://my-cool-app.com/get-user', { token }) 172 | } 173 | }) 174 | .then(user => { 175 | populatePageWithUser(user) 176 | initializeHelpIcon(user) 177 | }) 178 | } 179 | ``` 180 | 181 | --- 182 | 183 | ```javascript 184 | function populatePageWithUser (user) { 185 | if (user == null) { 186 | document.getElementById('user-name').innerHTML = 'Visitante' 187 | document.getElementById('status-message').innerHTML = 'Cadastre-se aqui!' 188 | } 189 | else { 190 | document.getElementById('user-name').innerHTML = user.name 191 | document.getElementById('status-message').innerHTML = user.statusMessage 192 | } 193 | } 194 | 195 | function initializeHelpIcon (user) { 196 | let helpText 197 | if (user == null) { 198 | helpText = `Olá Visitante! Como posso te ajudar?` 199 | } 200 | else { 201 | helpText = `Olá ${user.name}! Como posso te ajudar?` 202 | } 203 | 204 | document.getElementById('help-message-text').innerHTML = helpText 205 | } 206 | ``` 207 | 208 | --- 209 | 210 | ```javascript 211 | function populatePageWithUser (user) { 212 | * if (user == null) { 213 | * document.getElementById('user-name').innerHTML = 'Visitante' 214 | * document.getElementById('status-message').innerHTML = 'Cadastre-se aqui!' 215 | * } 216 | else { 217 | document.getElementById('user-name').innerHTML = user.name 218 | document.getElementById('status-message').innerHTML = user.statusMessage 219 | } 220 | } 221 | 222 | function initializeHelpIcon (user) { 223 | let helpText 224 | * if (user == null) { 225 | * helpText = `Olá Visitante! Como posso te ajudar?` 226 | * } 227 | else { 228 | helpText = `Olá ${user.name}! Como posso te ajudar?` 229 | } 230 | 231 | document.getElementById('help-message-text').innerHTML = helpText 232 | } 233 | ``` 234 | 235 | --- 236 | 237 | ```javascript 238 | document.onload = onPageLoad 239 | 240 | function onPageLoad () { 241 | Promise.resolve(localStorage) 242 | .then(({ token }) => { 243 | if (token) { 244 | return fetch('https://my-cool-app.com/get-user', { token }) 245 | } 246 | else { 247 | return makeNullUser() 248 | } 249 | }) 250 | .then(user => { 251 | populatePageWithUser(user) 252 | initializeHelpIcon(user) 253 | }) 254 | } 255 | 256 | function makeNullUser () { 257 | return { 258 | name: 'Visitante', 259 | statusMessage: 'Cadastre-se aqui!' 260 | } 261 | } 262 | ``` 263 | 264 | --- 265 | 266 | ```javascript 267 | function populatePageWithUser (user) { 268 | document.getElementById('user-name').innerHTML = user.name 269 | document.getElementById('status-message').innerHTML = user.statusMessage 270 | } 271 | 272 | function initializeHelpIcon (user) { 273 | const helpText = `Olá ${user.name}! Como posso te ajudar?` 274 | document.getElementById('help-message-text').innerHTML = helpText 275 | } 276 | ``` 277 | 278 | --- 279 | 280 | # Strategy 281 | 282 | Eventualmente nossa aplicacão terá comportamentos variaveis de acordo com o contexto de execucão. 283 | Nossa solucão acaba sempre sendo usar um `switch/case` ou um conjunto de `if/else if`: 284 | 285 | ```javascript 286 | if (auth.email && auth.password) { 287 | return fetch('/api/sessions') 288 | .then(res => res.json()) 289 | .then(...) 290 | } else if (auth.api_key) { 291 | return fetch('/api/authenticate') 292 | .then(res => res.json()) 293 | .then(...) 294 | } else if (auth.public_key) { 295 | return fetch('/api/authenticate') 296 | .then(res => res.json()) 297 | .then(...) 298 | } else { 299 | return Promise.reject(new Error('Invalid Authentication')) 300 | } 301 | ``` 302 | 303 | --- 304 | 305 | Podemos modelar esta variacão de comportamento em vários objetos de estrategia: 306 | 307 | ```javascript 308 | class LoginStrategy { 309 | constructor (params) { 310 | this.params 311 | } 312 | 313 | canAuthenticate() { 314 | return this.params.login && this.params.password 315 | } 316 | 317 | execute(params) { 318 | return fetch('/api/sessions') 319 | .then(res => res.json()) 320 | } 321 | } 322 | ``` 323 | 324 | --- 325 | 326 | ```javascript 327 | class ApiStrategy { 328 | constructor (params) { 329 | this.params 330 | } 331 | 332 | canAuthenticate() { 333 | return this.params.api_key && true 334 | } 335 | 336 | execute(params) { 337 | return fetch('/api/sessions') 338 | .then(res => res.json()) 339 | } 340 | } 341 | 342 | function selectStrategy(params) { 343 | const strategies = [LoginStrategy, ApiStrategy, ...] 344 | 345 | for (StrategyClass in strategies) 346 | const strategy = new StrategyClass(params) 347 | 348 | if (strategy.canAuthenticate()) { 349 | return strategy.execute() 350 | } 351 | } 352 | } 353 | ``` 354 | 355 | --- 356 | 357 | Mas poxa... Orientação a objetos faz os objetos ficarem muito acoplados 358 | e difíceis de testar! 359 | 360 | -- 361 | 362 | # Dependency Injection and Factories 363 | 364 | Quando isso acontecer, por que não passar as dependências do seu projeto 365 | durante a construção do seu objeto? 366 | 367 | --- 368 | 369 | ```javascript 370 | class DownloadManager { 371 | constructor (folder) { 372 | this.folder = folder 373 | } 374 | 375 | downloadFile (url, filename) { 376 | const filePath = path.join(this.folder, filename) 377 | return fetch(url) 378 | .then(data => data.json()) 379 | .then(contents => fs.writeFileAsync(filePath, contents)) 380 | } 381 | 382 | listDownloadedFiles () { 383 | return fs.readdirAsync(this.folder) 384 | } 385 | } 386 | ``` 387 | 388 | --- 389 | 390 | ```javascript 391 | class DownloadManager { 392 | constructor (folder) { 393 | this.folder = folder 394 | } 395 | 396 | downloadFile (url, filename) { 397 | * const filePath = path.join(this.folder, filename) 398 | * return fetch(url) 399 | .then(data => data.json()) 400 | * .then(contents => fs.writeFileAsync(filePath, contents)) 401 | } 402 | 403 | listDownloadedFiles () { 404 | * return fs.readdirAsync(this.folder) 405 | } 406 | } 407 | ``` 408 | 409 | --- 410 | 411 | ```javascript 412 | class DownloadManager { 413 | constructor (fetch, readdir, pathJoin, writeFile, folder) { 414 | this.fetch = fetch 415 | this.readdir = readdir 416 | this.pathJoin = pathJoin 417 | this.writeFile = writeFile 418 | this.folder = folder 419 | } 420 | 421 | downloadFile (url, filename) { 422 | const filePath = this.pathJoin(this.folder, filename) 423 | return this.fetch(url) 424 | .then(data => data.json()) 425 | .then(contents => this.writeFile(filePath, contents)) 426 | } 427 | 428 | listDownloadedFiles () { 429 | return readdir(this.folder) 430 | } 431 | } 432 | 433 | ``` 434 | 435 | --- 436 | 437 | Poxa, mas são muitas dependências para se passar durante a criação de um 438 | objeto. Isso é bastante trabalhoso. 439 | 440 | -- 441 | 442 | # Repository Pattern 443 | 444 | Sim porque estamos usando argumentos, mas não necessariamente. Com o repository 445 | pattern, você pode manter instâncias de todas as dependências do seu projeto em 446 | um objeto, o qual você pode passar como referência para a sua função ou na 447 | construção do seu objeto. 448 | 449 | --- 450 | 451 | ```javascript 452 | const repository = initializeServices() 453 | const downloader = new DownloadManager(repository) 454 | 455 | class DownloadManager { 456 | constructor ({ fetch, readdir, pathJoin, writeFile, folder }) { 457 | this.fetch = fetch 458 | this.readdir = readdir 459 | this.pathJoin = pathJoin 460 | this.writeFile = writeFile 461 | this.folder = folder 462 | } 463 | 464 | ... 465 | } 466 | 467 | ``` 468 | 469 | --- 470 | 471 | Também, de vez em quando, queremos utilizar um objeto ou função que já existe, mas só pra adicionar uma característica... 472 | 473 | -- 474 | 475 | # Decorator 476 | 477 | Nessas situações, a gente pode criar um objeto obedecendo à mesma interface do objeto a ser modificado, repassando (ou não) as chamadas para ele. 478 | 479 | --- 480 | 481 | ```javascript 482 | handleNewUser(console, { username: 'deschamps', password: 'renatinha<3' }) 483 | 484 | function handleNewUser(log, user) { 485 | log.info('New user is', user) 486 | } 487 | 488 | // New user is { username: 'deschamps', password: 'renatinha<3' } 489 | ``` 490 | 491 | --- 492 | 493 | ```javascript 494 | handleNewUser(console, { username: 'deschamps', password: 'renatinha<3' }) 495 | 496 | function handleNewUser(log, user) { 497 | log.info('New user is', user) 498 | } 499 | 500 | *// New user is { username: 'deschamps', password: 'renatinha<3' } 501 | ``` 502 | 503 | --- 504 | 505 | ```javascript 506 | const log = makeSafeLog(console) 507 | handleNewUser(log, { username: 'deschamps', password: 'renatinha<3' }) 508 | 509 | function makeSafeLog (log) { 510 | return { 511 | info (...args) { 512 | const safeArgs = [] 513 | for (const arg of args) { 514 | if (Object.keys(arg).includes('password')) { 515 | const maskedArg = Object.assign({}, arg, { password: '***' }) 516 | safeArgs.push(maskedArg) 517 | } 518 | else { 519 | safeArgs.push(arg) 520 | } 521 | } 522 | log.info(...safeArgs) 523 | } 524 | } 525 | } 526 | 527 | // New user is { username: 'deschamps', password: '***' } 528 | ``` 529 | 530 | --- 531 | 532 | 533 | # Adapter 534 | 535 | Eventualmente temos objetos de negócios com interfaces incompatíveis, porém 536 | percebemos que conseguimos adaptá-lo para a interface desejada. Nessas 537 | situacões o padrão de Adapter é muito útil. 538 | 539 | --- 540 | 541 | ```javascript 542 | class ArrayListAdapter { 543 | constructor(array) { 544 | this.array = array 545 | } 546 | 547 | length() { 548 | return this.array.length 549 | } 550 | 551 | getItem(index) { 552 | return this.array[index] 553 | } 554 | } 555 | 556 | class MapListAdapter { 557 | constructor(map) { 558 | this.map = map 559 | } 560 | 561 | length() { 562 | return this.map.size() 563 | } 564 | 565 | getItem(index) { 566 | return this.map.values()[index] 567 | } 568 | } 569 | ``` 570 | 571 | --- 572 | 573 | # Conclusão 574 | 575 | Orientação a objetos se trata de uma maneira conveniente de utilizar polimorfismo 576 | 577 | -- 578 | 579 | Ou seja, em vez de você ficar construindo o seu código com `if (...) {} else {}`, você consegue construir toda a lógica utilizando apenas objetos e chamadas de métodos. 580 | 581 | --- 582 | 583 | # Já ouviu falar de Smalltalk? 584 | 585 | É uma linguagem inteiramente orientada a objetos, que consegue funciona inteiramente a base de chamadas polimórfica de métodos. Não existe nem mesmo o `if` statement na linguagem. 586 | 587 | -- 588 | 589 | JavaScript é uma linguagem multiparadigma. Portanto, o caminho para se escrever código legível é conhecer o melhor do que a linguagem tem a oferecer, e utilizar a arquitetura certa para resolver o problema certo. 590 | 591 | -- 592 | 593 | Isso, muitas vezes, significa escrever código parte funcional, parte orientado a objetos. 594 | 595 | --- 596 | 597 | class: impact 598 | 599 | # Obrigado! 600 | 601 | Slides disponíveis em https://github.com/thalesmello/slides/tree/master/2017/oo-in-js-the-good-parts 602 | 603 | ## Perguntas? 604 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "theconf-expanding-async-in-javascript", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ansi-regex": { 8 | "version": "2.1.1", 9 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 10 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 11 | }, 12 | "ansi-styles": { 13 | "version": "2.2.1", 14 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 15 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 16 | }, 17 | "any-promise": { 18 | "version": "1.3.0", 19 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 20 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 21 | }, 22 | "app-root-path": { 23 | "version": "2.0.1", 24 | "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", 25 | "integrity": "sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y=" 26 | }, 27 | "array-uniq": { 28 | "version": "1.0.2", 29 | "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz", 30 | "integrity": "sha1-X8w3OSB3VyPP1k1lxkvvU7+eum0=" 31 | }, 32 | "arrify": { 33 | "version": "1.0.1", 34 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 35 | "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" 36 | }, 37 | "async": { 38 | "version": "1.0.0", 39 | "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", 40 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" 41 | }, 42 | "babel-code-frame": { 43 | "version": "6.26.0", 44 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", 45 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", 46 | "requires": { 47 | "chalk": "1.1.3", 48 | "esutils": "2.0.2", 49 | "js-tokens": "3.0.2" 50 | } 51 | }, 52 | "babel-helper-builder-binary-assignment-operator-visitor": { 53 | "version": "6.24.1", 54 | "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", 55 | "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", 56 | "dev": true, 57 | "requires": { 58 | "babel-helper-explode-assignable-expression": "6.24.1", 59 | "babel-runtime": "6.26.0", 60 | "babel-types": "6.26.0" 61 | } 62 | }, 63 | "babel-helper-explode-assignable-expression": { 64 | "version": "6.24.1", 65 | "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", 66 | "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", 67 | "dev": true, 68 | "requires": { 69 | "babel-runtime": "6.26.0", 70 | "babel-traverse": "6.26.0", 71 | "babel-types": "6.26.0" 72 | } 73 | }, 74 | "babel-helper-function-name": { 75 | "version": "6.24.1", 76 | "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", 77 | "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", 78 | "requires": { 79 | "babel-helper-get-function-arity": "6.24.1", 80 | "babel-runtime": "6.26.0", 81 | "babel-template": "6.26.0", 82 | "babel-traverse": "6.26.0", 83 | "babel-types": "6.26.0" 84 | } 85 | }, 86 | "babel-helper-get-function-arity": { 87 | "version": "6.24.1", 88 | "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", 89 | "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", 90 | "requires": { 91 | "babel-runtime": "6.26.0", 92 | "babel-types": "6.26.0" 93 | } 94 | }, 95 | "babel-helper-remap-async-to-generator": { 96 | "version": "6.24.1", 97 | "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", 98 | "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", 99 | "requires": { 100 | "babel-helper-function-name": "6.24.1", 101 | "babel-runtime": "6.26.0", 102 | "babel-template": "6.26.0", 103 | "babel-traverse": "6.26.0", 104 | "babel-types": "6.26.0" 105 | } 106 | }, 107 | "babel-messages": { 108 | "version": "6.23.0", 109 | "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", 110 | "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", 111 | "requires": { 112 | "babel-runtime": "6.26.0" 113 | } 114 | }, 115 | "babel-plugin-syntax-async-functions": { 116 | "version": "6.13.0", 117 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", 118 | "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", 119 | "dev": true 120 | }, 121 | "babel-plugin-syntax-async-generators": { 122 | "version": "6.13.0", 123 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", 124 | "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=" 125 | }, 126 | "babel-plugin-syntax-exponentiation-operator": { 127 | "version": "6.13.0", 128 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", 129 | "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", 130 | "dev": true 131 | }, 132 | "babel-plugin-syntax-object-rest-spread": { 133 | "version": "6.13.0", 134 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", 135 | "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", 136 | "dev": true 137 | }, 138 | "babel-plugin-syntax-trailing-function-commas": { 139 | "version": "6.22.0", 140 | "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", 141 | "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", 142 | "dev": true 143 | }, 144 | "babel-plugin-transform-async-generator-functions": { 145 | "version": "6.24.1", 146 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", 147 | "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", 148 | "requires": { 149 | "babel-helper-remap-async-to-generator": "6.24.1", 150 | "babel-plugin-syntax-async-generators": "6.13.0", 151 | "babel-runtime": "6.26.0" 152 | } 153 | }, 154 | "babel-plugin-transform-async-to-generator": { 155 | "version": "6.24.1", 156 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", 157 | "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", 158 | "dev": true, 159 | "requires": { 160 | "babel-helper-remap-async-to-generator": "6.24.1", 161 | "babel-plugin-syntax-async-functions": "6.13.0", 162 | "babel-runtime": "6.26.0" 163 | } 164 | }, 165 | "babel-plugin-transform-exponentiation-operator": { 166 | "version": "6.24.1", 167 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", 168 | "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", 169 | "dev": true, 170 | "requires": { 171 | "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", 172 | "babel-plugin-syntax-exponentiation-operator": "6.13.0", 173 | "babel-runtime": "6.26.0" 174 | } 175 | }, 176 | "babel-plugin-transform-object-rest-spread": { 177 | "version": "6.26.0", 178 | "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", 179 | "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", 180 | "dev": true, 181 | "requires": { 182 | "babel-plugin-syntax-object-rest-spread": "6.13.0", 183 | "babel-runtime": "6.26.0" 184 | } 185 | }, 186 | "babel-preset-stage-3": { 187 | "version": "6.24.1", 188 | "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", 189 | "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", 190 | "dev": true, 191 | "requires": { 192 | "babel-plugin-syntax-trailing-function-commas": "6.22.0", 193 | "babel-plugin-transform-async-generator-functions": "6.24.1", 194 | "babel-plugin-transform-async-to-generator": "6.24.1", 195 | "babel-plugin-transform-exponentiation-operator": "6.24.1", 196 | "babel-plugin-transform-object-rest-spread": "6.26.0" 197 | } 198 | }, 199 | "babel-runtime": { 200 | "version": "6.26.0", 201 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", 202 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", 203 | "requires": { 204 | "core-js": "2.5.1", 205 | "regenerator-runtime": "0.11.0" 206 | } 207 | }, 208 | "babel-template": { 209 | "version": "6.26.0", 210 | "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", 211 | "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", 212 | "requires": { 213 | "babel-runtime": "6.26.0", 214 | "babel-traverse": "6.26.0", 215 | "babel-types": "6.26.0", 216 | "babylon": "6.18.0", 217 | "lodash": "4.17.4" 218 | } 219 | }, 220 | "babel-traverse": { 221 | "version": "6.26.0", 222 | "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", 223 | "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", 224 | "requires": { 225 | "babel-code-frame": "6.26.0", 226 | "babel-messages": "6.23.0", 227 | "babel-runtime": "6.26.0", 228 | "babel-types": "6.26.0", 229 | "babylon": "6.18.0", 230 | "debug": "2.6.9", 231 | "globals": "9.18.0", 232 | "invariant": "2.2.2", 233 | "lodash": "4.17.4" 234 | } 235 | }, 236 | "babel-types": { 237 | "version": "6.26.0", 238 | "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", 239 | "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", 240 | "requires": { 241 | "babel-runtime": "6.26.0", 242 | "esutils": "2.0.2", 243 | "lodash": "4.17.4", 244 | "to-fast-properties": "1.0.3" 245 | } 246 | }, 247 | "babylon": { 248 | "version": "6.18.0", 249 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", 250 | "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" 251 | }, 252 | "bluebird": { 253 | "version": "3.5.0", 254 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 255 | "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" 256 | }, 257 | "bson": { 258 | "version": "0.5.5", 259 | "resolved": "https://registry.npmjs.org/bson/-/bson-0.5.5.tgz", 260 | "integrity": "sha1-HWcl1ADw+/AnG/a6/I+hEmwpmDs=" 261 | }, 262 | "chalk": { 263 | "version": "1.1.3", 264 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 265 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 266 | "requires": { 267 | "ansi-styles": "2.2.1", 268 | "escape-string-regexp": "1.0.5", 269 | "has-ansi": "2.0.0", 270 | "strip-ansi": "3.0.1", 271 | "supports-color": "2.0.0" 272 | } 273 | }, 274 | "colors": { 275 | "version": "1.0.3", 276 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 277 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" 278 | }, 279 | "comedy": { 280 | "version": "1.2.0", 281 | "resolved": "https://registry.npmjs.org/comedy/-/comedy-1.2.0.tgz", 282 | "integrity": "sha512-JiAKtdoiqr3I0hk6C8ofVP38N/Jj3BYKqoaz5WJwpUTNJDNR4VCu/HFWkSpmLCzLTw+Aa+1bWA99m4eTRwIahw==", 283 | "requires": { 284 | "app-root-path": "2.0.1", 285 | "bluebird": "3.4.6", 286 | "bson": "0.5.5", 287 | "randomstring": "1.1.5", 288 | "require-dir": "0.3.2", 289 | "tosource": "1.0.0", 290 | "ts-node": "1.3.0", 291 | "typescript": "2.0.3", 292 | "underscore": "1.8.3", 293 | "underscore.string": "3.3.4", 294 | "winston": "2.2.0" 295 | }, 296 | "dependencies": { 297 | "bluebird": { 298 | "version": "3.4.6", 299 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.6.tgz", 300 | "integrity": "sha1-AdqNgh2HgT0ViWfnQ9X+bGLPjA8=" 301 | } 302 | } 303 | }, 304 | "core-js": { 305 | "version": "2.5.1", 306 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", 307 | "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" 308 | }, 309 | "cycle": { 310 | "version": "1.0.3", 311 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 312 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" 313 | }, 314 | "debug": { 315 | "version": "2.6.9", 316 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 317 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 318 | "requires": { 319 | "ms": "2.0.0" 320 | } 321 | }, 322 | "diff": { 323 | "version": "2.2.3", 324 | "resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz", 325 | "integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k=" 326 | }, 327 | "error-ex": { 328 | "version": "1.3.1", 329 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", 330 | "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", 331 | "requires": { 332 | "is-arrayish": "0.2.1" 333 | } 334 | }, 335 | "escape-string-regexp": { 336 | "version": "1.0.5", 337 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 338 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 339 | }, 340 | "esutils": { 341 | "version": "2.0.2", 342 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 343 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" 344 | }, 345 | "eyes": { 346 | "version": "0.1.8", 347 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 348 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 349 | }, 350 | "globals": { 351 | "version": "9.18.0", 352 | "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", 353 | "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" 354 | }, 355 | "has-ansi": { 356 | "version": "2.0.0", 357 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 358 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 359 | "requires": { 360 | "ansi-regex": "2.1.1" 361 | } 362 | }, 363 | "invariant": { 364 | "version": "2.2.2", 365 | "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", 366 | "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", 367 | "requires": { 368 | "loose-envify": "1.3.1" 369 | } 370 | }, 371 | "is-arrayish": { 372 | "version": "0.2.1", 373 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 374 | "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" 375 | }, 376 | "is-utf8": { 377 | "version": "0.2.1", 378 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 379 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 380 | }, 381 | "isstream": { 382 | "version": "0.1.2", 383 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 384 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 385 | }, 386 | "js-csp": { 387 | "version": "1.0.1", 388 | "resolved": "https://registry.npmjs.org/js-csp/-/js-csp-1.0.1.tgz", 389 | "integrity": "sha1-Z7N01E2eNTr59+5E+8YPUzgRfxI=", 390 | "requires": { 391 | "babel-runtime": "6.26.0", 392 | "lodash": "4.17.4" 393 | } 394 | }, 395 | "js-tokens": { 396 | "version": "3.0.2", 397 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", 398 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" 399 | }, 400 | "lodash": { 401 | "version": "4.17.4", 402 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 403 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 404 | }, 405 | "loose-envify": { 406 | "version": "1.3.1", 407 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", 408 | "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", 409 | "requires": { 410 | "js-tokens": "3.0.2" 411 | } 412 | }, 413 | "make-error": { 414 | "version": "1.3.0", 415 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz", 416 | "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=" 417 | }, 418 | "minimist": { 419 | "version": "1.2.0", 420 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 421 | "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" 422 | }, 423 | "mkdirp": { 424 | "version": "0.5.1", 425 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 426 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 427 | "requires": { 428 | "minimist": "0.0.8" 429 | }, 430 | "dependencies": { 431 | "minimist": { 432 | "version": "0.0.8", 433 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 434 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" 435 | } 436 | } 437 | }, 438 | "ms": { 439 | "version": "2.0.0", 440 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 441 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 442 | }, 443 | "parse-json": { 444 | "version": "2.2.0", 445 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 446 | "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", 447 | "requires": { 448 | "error-ex": "1.3.1" 449 | } 450 | }, 451 | "pinkie": { 452 | "version": "2.0.4", 453 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 454 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" 455 | }, 456 | "pkginfo": { 457 | "version": "0.3.1", 458 | "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", 459 | "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=" 460 | }, 461 | "randomstring": { 462 | "version": "1.1.5", 463 | "resolved": "https://registry.npmjs.org/randomstring/-/randomstring-1.1.5.tgz", 464 | "integrity": "sha1-bfBij3XL1ZMpMNn+OrTpVqGFGMM=", 465 | "requires": { 466 | "array-uniq": "1.0.2" 467 | } 468 | }, 469 | "regenerator-runtime": { 470 | "version": "0.11.0", 471 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", 472 | "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==" 473 | }, 474 | "require-dir": { 475 | "version": "0.3.2", 476 | "resolved": "https://registry.npmjs.org/require-dir/-/require-dir-0.3.2.tgz", 477 | "integrity": "sha1-wdXHXp+//eny5rM+OD209ZS1pqk=" 478 | }, 479 | "rxjs": { 480 | "version": "5.4.3", 481 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.3.tgz", 482 | "integrity": "sha512-fSNi+y+P9ss+EZuV0GcIIqPUK07DEaMRUtLJvdcvMyFjc9dizuDjere+A4V7JrLGnm9iCc+nagV/4QdMTkqC4A==", 483 | "requires": { 484 | "symbol-observable": "1.0.4" 485 | } 486 | }, 487 | "semver": { 488 | "version": "5.4.1", 489 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 490 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" 491 | }, 492 | "source-map": { 493 | "version": "0.5.7", 494 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 495 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" 496 | }, 497 | "source-map-support": { 498 | "version": "0.4.18", 499 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", 500 | "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", 501 | "requires": { 502 | "source-map": "0.5.7" 503 | } 504 | }, 505 | "sprintf-js": { 506 | "version": "1.1.1", 507 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", 508 | "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" 509 | }, 510 | "stack-trace": { 511 | "version": "0.0.10", 512 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 513 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 514 | }, 515 | "strip-ansi": { 516 | "version": "3.0.1", 517 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 518 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 519 | "requires": { 520 | "ansi-regex": "2.1.1" 521 | } 522 | }, 523 | "strip-bom": { 524 | "version": "2.0.0", 525 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 526 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 527 | "requires": { 528 | "is-utf8": "0.2.1" 529 | } 530 | }, 531 | "strip-json-comments": { 532 | "version": "2.0.1", 533 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 534 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 535 | }, 536 | "supports-color": { 537 | "version": "2.0.0", 538 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 539 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 540 | }, 541 | "symbol-observable": { 542 | "version": "1.0.4", 543 | "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.4.tgz", 544 | "integrity": "sha1-Kb9hXUqnEhvdiYsi1LP5vE4qoD0=" 545 | }, 546 | "to-fast-properties": { 547 | "version": "1.0.3", 548 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", 549 | "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" 550 | }, 551 | "top-level-await": { 552 | "version": "1.1.0", 553 | "resolved": "https://registry.npmjs.org/top-level-await/-/top-level-await-1.1.0.tgz", 554 | "integrity": "sha1-tnEFo5FgEnmyublDbC4K0Q9hruA=", 555 | "requires": { 556 | "semver": "5.4.1" 557 | } 558 | }, 559 | "tosource": { 560 | "version": "1.0.0", 561 | "resolved": "https://registry.npmjs.org/tosource/-/tosource-1.0.0.tgz", 562 | "integrity": "sha1-QtiN0RZhi88A1hBt1URvNCeQL/E=" 563 | }, 564 | "ts-node": { 565 | "version": "1.3.0", 566 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-1.3.0.tgz", 567 | "integrity": "sha1-CmiqCAC333oritbn40sPlhI4elI=", 568 | "requires": { 569 | "arrify": "1.0.1", 570 | "chalk": "1.1.3", 571 | "diff": "2.2.3", 572 | "make-error": "1.3.0", 573 | "minimist": "1.2.0", 574 | "mkdirp": "0.5.1", 575 | "pinkie": "2.0.4", 576 | "source-map-support": "0.4.18", 577 | "tsconfig": "5.0.3", 578 | "xtend": "4.0.1" 579 | } 580 | }, 581 | "tsconfig": { 582 | "version": "5.0.3", 583 | "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz", 584 | "integrity": "sha1-X0J45wGACWeo/Dg/0ZZIh48qbjo=", 585 | "requires": { 586 | "any-promise": "1.3.0", 587 | "parse-json": "2.2.0", 588 | "strip-bom": "2.0.0", 589 | "strip-json-comments": "2.0.1" 590 | } 591 | }, 592 | "typescript": { 593 | "version": "2.0.3", 594 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.0.3.tgz", 595 | "integrity": "sha1-M97J6uhrju4yfdQZygUMhTyr1RQ=" 596 | }, 597 | "underscore": { 598 | "version": "1.8.3", 599 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", 600 | "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" 601 | }, 602 | "underscore.string": { 603 | "version": "3.3.4", 604 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", 605 | "integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=", 606 | "requires": { 607 | "sprintf-js": "1.1.1", 608 | "util-deprecate": "1.0.2" 609 | } 610 | }, 611 | "util-deprecate": { 612 | "version": "1.0.2", 613 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 614 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 615 | }, 616 | "winston": { 617 | "version": "2.2.0", 618 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.2.0.tgz", 619 | "integrity": "sha1-LIU92Hq1UqjoSF1yy7+aIobwKbc=", 620 | "requires": { 621 | "async": "1.0.0", 622 | "colors": "1.0.3", 623 | "cycle": "1.0.3", 624 | "eyes": "0.1.8", 625 | "isstream": "0.1.2", 626 | "pkginfo": "0.3.1", 627 | "stack-trace": "0.0.10" 628 | } 629 | }, 630 | "xtend": { 631 | "version": "4.0.1", 632 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 633 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 634 | } 635 | } 636 | } 637 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/slides.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Beyond Promises\n", 12 | "\n", 13 | "## Async Patterns in JavaScript\n", 14 | "\n", 15 | "
\n", 16 | "
\n", 17 | "
\n", 18 | "\n", 19 | "
by Thales Mello
" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": { 25 | "slideshow": { 26 | "slide_type": "slide" 27 | } 28 | }, 29 | "source": [ 30 | "# Let's start with a story\n", 31 | "\n", 32 | "Weird JavaScript behavior had always kept me from sharing any interest with Node.\n", 33 | "\n", 34 | "But in 2015, I was helping friend build a chat microservice for his app\n", 35 | "\n", 36 | "The alternative we chose to solve the problem as a combination of [Node](https://nodejs.org/en/) + [SocketIO](https://socket.io/)..." 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "slideshow": { 43 | "slide_type": "slide" 44 | } 45 | }, 46 | "source": [ 47 | "and it I became really impressed with the final results!\n", 48 | "\n", 49 | "But frankly, the programming environment felt very awkward.\n", 50 | "\n", 51 | "Anyhow, it was enough to stir up my interest about asynchronous programming." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "slideshow": { 58 | "slide_type": "slide" 59 | } 60 | }, 61 | "source": [ 62 | "# What exectly do you mean with awkward programming environment?\n", 63 | "\n", 64 | "Callbacks." 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "slideshow": { 71 | "slide_type": "slide" 72 | } 73 | }, 74 | "source": [ 75 | "In Node, callbacks are the builtin way of invoking asynchronous code, and in the first years of Node, it was the only way.\n", 76 | "\n", 77 | "And because asynchronous programming was the whole point of using Node, I frankly used callbacks for everything." 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": { 83 | "slideshow": { 84 | "slide_type": "slide" 85 | } 86 | }, 87 | "source": [ 88 | "# Node\n", 89 | "\n", 90 | "- Allows us to create fast servers relying on JavaScript's event driven approach to programming\n", 91 | "- But working with async programming can be hard" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": { 97 | "slideshow": { 98 | "slide_type": "slide" 99 | } 100 | }, 101 | "source": [ 102 | "# How exactly callbacks work?" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 32, 108 | "metadata": { 109 | "slideshow": { 110 | "slide_type": "slide" 111 | } 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "var sleep = time => {\n", 116 | " let start = new Date().getTime()\n", 117 | " let expire = start + time;\n", 118 | "\n", 119 | " while (new Date().getTime() < expire);\n", 120 | "}" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 33, 126 | "metadata": { 127 | "slideshow": { 128 | "slide_type": "slide" 129 | } 130 | }, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "Hello\n", 137 | "world!\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "// Consider this sync example. The 'world' is delayed when compared\n", 143 | "// to the first line. But it happens at the cost of blocking JavaScript's\n", 144 | "// single thread.\n", 145 | "console.log('Hello')\n", 146 | "sleep(1000)\n", 147 | "console.log('world!')" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 34, 153 | "metadata": { 154 | "slideshow": { 155 | "slide_type": "slide" 156 | } 157 | }, 158 | "outputs": [ 159 | { 160 | "name": "stdout", 161 | "output_type": "stream", 162 | "text": [ 163 | "Hello\n", 164 | "world!\n" 165 | ] 166 | } 167 | ], 168 | "source": [ 169 | "var progress = fn => {\n", 170 | " var handle = setInterval(() => process.stdout.write('.'), 50)\n", 171 | " fn(() => clearTimeout(handle))\n", 172 | "}\n", 173 | "\n", 174 | "progress(done => {\n", 175 | " console.log('Hello')\n", 176 | " sleep(1000)\n", 177 | " console.log('world!')\n", 178 | " done()\n", 179 | "})" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 35, 185 | "metadata": { 186 | "scrolled": true, 187 | "slideshow": { 188 | "slide_type": "slide" 189 | } 190 | }, 191 | "outputs": [ 192 | { 193 | "name": "stdout", 194 | "output_type": "stream", 195 | "text": [ 196 | "Hello\n", 197 | "..................world!\n" 198 | ] 199 | } 200 | ], 201 | "source": [ 202 | "var timeout = (time, fn) => setTimeout(fn, time)\n", 203 | "\n", 204 | "progress(done => {\n", 205 | " console.log('Hello')\n", 206 | " timeout(1000, () => {\n", 207 | " console.log('world!')\n", 208 | " done()\n", 209 | " })\n", 210 | "})" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": { 216 | "slideshow": { 217 | "slide_type": "slide" 218 | } 219 | }, 220 | "source": [ 221 | "# Let's work with a two servers example " 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 39, 227 | "metadata": { 228 | "slideshow": { 229 | "slide_type": "slide" 230 | } 231 | }, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "A: Booting up system\n", 238 | "B: Booting up system\n", 239 | ".B: Server up and running\n", 240 | "..................A: Checking network connection\n", 241 | ".........A: Request complex computation\n", 242 | "B: Starting heavy computation\n", 243 | "......................................A: Computation returned 42\n" 244 | ] 245 | } 246 | ], 247 | "source": [ 248 | "progress(done => {\n", 249 | " var queueCallback, serverHandler\n", 250 | " console.log(\"A: Booting up system\")\n", 251 | " timeout(1000, () => {\n", 252 | " console.log(\"A: Checking network connection\")\n", 253 | " timeout(500, () => {\n", 254 | " console.log(\"A: Request complex computation\")\n", 255 | " sendRequest(value => {\n", 256 | " console.log(\"A: Computation returned \" + value)\n", 257 | " done()\n", 258 | " })\n", 259 | " })\n", 260 | " })\n", 261 | "\n", 262 | " console.log(\"B: Booting up system\")\n", 263 | " timeout(100, () => {\n", 264 | " console.log(\"B: Server up and running\")\n", 265 | " serverHandler = (callback) => {\n", 266 | " console.log(\"B: Starting heavy computation\")\n", 267 | " timeout(2000, () => callback(42))\n", 268 | " }\n", 269 | " if (queueCallback) { serverHandler(queueCallback) }\n", 270 | " })\n", 271 | "\n", 272 | " function sendRequest(callback) {\n", 273 | " if (serverHandler) { serverHandler(callback) }\n", 274 | " else { queueCallback = callback }\n", 275 | " }\n", 276 | "})" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 40, 282 | "metadata": { 283 | "collapsed": true, 284 | "slideshow": { 285 | "slide_type": "slide" 286 | } 287 | }, 288 | "outputs": [], 289 | "source": [ 290 | "function serverA(bootTime, done) {\n", 291 | " console.log(\"A: Booting up system...\")\n", 292 | " timeout(bootTime, checkNetwork)\n", 293 | "\n", 294 | " function checkNetwork() {\n", 295 | " console.log(\"A: Checking network connection\")\n", 296 | " timeout(500, sendRequest)\n", 297 | " }\n", 298 | "\n", 299 | " function sendRequest() {\n", 300 | " console.log(\"A: Request complex computation\")\n", 301 | " sendNetworkRequest(callback)\n", 302 | " }\n", 303 | "\n", 304 | " function callback(value) {\n", 305 | " console.log(\"A: Computation returned \" + value)\n", 306 | " done()\n", 307 | " }\n", 308 | "}" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 41, 314 | "metadata": { 315 | "collapsed": true, 316 | "slideshow": { 317 | "slide_type": "slide" 318 | } 319 | }, 320 | "outputs": [], 321 | "source": [ 322 | "function serverB(bootTime) {\n", 323 | " console.log(\"B: Booting up system...\")\n", 324 | " timeout(bootTime, listenRequests)\n", 325 | "\n", 326 | " function listenRequests() {\n", 327 | " console.log(\"B: Server up and running\")\n", 328 | " serverHandler = handler\n", 329 | "\n", 330 | " if (queueCallback) {\n", 331 | " serverHandler(queueCallback)\n", 332 | " queueCallback = null\n", 333 | " }\n", 334 | " }\n", 335 | "\n", 336 | " function handler(callback) {\n", 337 | " console.log(\"B: Starting heavy computation\")\n", 338 | " timeout(2000, () => callback(42))\n", 339 | " }\n", 340 | "}" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 9, 346 | "metadata": { 347 | "slideshow": { 348 | "slide_type": "slide" 349 | } 350 | }, 351 | "outputs": [ 352 | { 353 | "name": "stdout", 354 | "output_type": "stream", 355 | "text": [ 356 | "A: Booting up system...\n", 357 | "B: Booting up system...\n", 358 | ".B: Server up and running\n", 359 | ".................A: Checking network connection\n", 360 | "..........A: Request complex computation\n", 361 | "B: Starting heavy computation\n", 362 | "......................................A: Computation returned 42\n" 363 | ] 364 | } 365 | ], 366 | "source": [ 367 | "function sendNetworkRequest(callback) {\n", 368 | " if(serverHandler) {\n", 369 | " serverHandler(callback)\n", 370 | " } else {\n", 371 | " queueCallback = callback\n", 372 | " }\n", 373 | "}\n", 374 | "\n", 375 | "var queueCallback = null\n", 376 | "var serverHandler = null\n", 377 | "\n", 378 | "progress((done) => {\n", 379 | " serverA(1000, done)\n", 380 | " serverB(100)\n", 381 | "})" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "slideshow": { 388 | "slide_type": "slide" 389 | } 390 | }, 391 | "source": [ 392 | "# Wait, have you heard about promises?\n", 393 | "\n", 394 | "A quick Promises explanation, according to the Promises/A+ specification, for people not familiar with the concept. We are going to explain how we return an object that will be resolved or will fail in the future, and how actions can be chained with the `.then()` method." 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 10, 400 | "metadata": { 401 | "slideshow": { 402 | "slide_type": "slide" 403 | } 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "..................Hello world!\n", 411 | ".........." 412 | ] 413 | }, 414 | { 415 | "data": { 416 | "text/plain": [ 417 | "'I will be back'" 418 | ] 419 | }, 420 | "execution_count": 10, 421 | "metadata": {}, 422 | "output_type": "execute_result" 423 | } 424 | ], 425 | "source": [ 426 | "var { promisify } = require('util')\n", 427 | "var delay = promisify(setTimeout)\n", 428 | "var asyncProgress = fn => {\n", 429 | " var handle = setInterval(() => process.stdout.write('.'), 50)\n", 430 | " return new Promise((resolve, reject) => {\n", 431 | " fn().then(value => {\n", 432 | " clearTimeout(handle)\n", 433 | " resolve(value)\n", 434 | " }).catch(error => {\n", 435 | " clearTimeout(handle)\n", 436 | " reject(error)\n", 437 | " })\n", 438 | " })\n", 439 | "}\n", 440 | "\n", 441 | "asyncProgress(() => {\n", 442 | " return delay(1000)\n", 443 | " .then(() => {\n", 444 | " console.log('Hello world!')\n", 445 | " return \"I will be back\"\n", 446 | " })\n", 447 | " .then(value => delay(500).then(() => value))\n", 448 | "})\n" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 11, 454 | "metadata": { 455 | "collapsed": true, 456 | "slideshow": { 457 | "slide_type": "slide" 458 | } 459 | }, 460 | "outputs": [], 461 | "source": [ 462 | "function getServerA(bootTime) {\n", 463 | " console.log(\"A: Booting up system...\")\n", 464 | "\n", 465 | " return Promise.delay(bootTime)\n", 466 | " .then(checkNetwork)\n", 467 | " .then(() => ({ sendRequest }))\n", 468 | "\n", 469 | " function checkNetwork() {\n", 470 | " console.log(\"A: Checking network connection\")\n", 471 | " return delay(500)\n", 472 | " }\n", 473 | "\n", 474 | " function sendRequest(remoteServer) {\n", 475 | " console.log(\"A: Request complex computation\")\n", 476 | " return remoteServer\n", 477 | " .compute(2)\n", 478 | " .then(value => {\n", 479 | " console.log\n", 480 | " console.log(`A: Computation returned ${value}`)\n", 481 | " })\n", 482 | " }\n", 483 | "}" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 12, 489 | "metadata": { 490 | "slideshow": { 491 | "slide_type": "slide" 492 | } 493 | }, 494 | "outputs": [], 495 | "source": [ 496 | "function getServerB(bootTime) {\n", 497 | " console.log(\"B: Booting up system...\")\n", 498 | "\n", 499 | " return Promise.delay(bootTime).then(() => {\n", 500 | " console.log(\"B: Server up and running\")\n", 501 | " return { compute }\n", 502 | " })\n", 503 | "\n", 504 | " function compute(value) {\n", 505 | " console.log(\"B: Starting heavy computation\")\n", 506 | " return delay(2000).then(() => 40 + value)\n", 507 | " }\n", 508 | "}" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": 13, 514 | "metadata": { 515 | "slideshow": { 516 | "slide_type": "slide" 517 | } 518 | }, 519 | "outputs": [ 520 | { 521 | "name": "stdout", 522 | "output_type": "stream", 523 | "text": [ 524 | "A: Booting up system...\n", 525 | "B: Booting up system...\n", 526 | ".B: Server up and running\n", 527 | "..................A: Checking network connection\n", 528 | ".........A: Request complex computation\n", 529 | "B: Starting heavy computation\n", 530 | "......................................A: Computation returned 42\n" 531 | ] 532 | } 533 | ], 534 | "source": [ 535 | "var Promise = require('bluebird')\n", 536 | "\n", 537 | "asyncProgress(main)\n", 538 | "\n", 539 | "function main () {\n", 540 | " return Promise.props({\n", 541 | " serverA: getServerA(1000),\n", 542 | " serverB: getServerB(100)\n", 543 | " }).then(({ serverA, serverB }) => {\n", 544 | " return serverA.sendRequest(serverB)\n", 545 | " })\n", 546 | "}" 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": { 552 | "slideshow": { 553 | "slide_type": "slide" 554 | } 555 | }, 556 | "source": [ 557 | "# Async/Await as an imperative, easier to read approach\n", 558 | "\n", 559 | "Promises end up being a more functional programming approach, but a little more difficult to read for folks used to imperative programming. After function generators (aka coroutines) were introduced in the ES2015 specification, people started using it to replicate the Async/Await feature from C#, to be able to await Promises. We are going to see how it was used, and how it evolved to be officially adopted in the ES2017 specification.\n", 560 | "\n", 561 | "We are also going to talk about Async/Await limitation of awaiting a single Promise at a time, and how other approaches might solve this problem." 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": 14, 567 | "metadata": { 568 | "slideshow": { 569 | "slide_type": "slide" 570 | } 571 | }, 572 | "outputs": [], 573 | "source": [ 574 | "async function getServerA(bootTime) {\n", 575 | " console.log(\"A: Booting up system...\")\n", 576 | " await delay(bootTime)\n", 577 | " await checkNetwork()\n", 578 | "\n", 579 | " return { sendRequest }\n", 580 | "\n", 581 | " function checkNetwork() {\n", 582 | " console.log(\"A: Checking network connection\")\n", 583 | " return delay(500)\n", 584 | " }\n", 585 | "\n", 586 | " async function sendRequest(remoteServer) {\n", 587 | " console.log(\"A: Request complex computation\")\n", 588 | " var value = await remoteServer.compute(2)\n", 589 | " console.log(`A: Computation returned ${value}`)\n", 590 | " }\n", 591 | "}" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 15, 597 | "metadata": { 598 | "slideshow": { 599 | "slide_type": "slide" 600 | } 601 | }, 602 | "outputs": [], 603 | "source": [ 604 | "async function getServerB(bootTime) {\n", 605 | " console.log(\"B: Booting up system...\")\n", 606 | " await delay(bootTime)\n", 607 | " console.log(\"B: Server up and running\")\n", 608 | " return { compute }\n", 609 | "\n", 610 | " async function compute(value) {\n", 611 | " console.log(\"B: Starting heavy computation\")\n", 612 | " await delay(2000)\n", 613 | " return 40 + value\n", 614 | " }\n", 615 | "}" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 16, 621 | "metadata": { 622 | "slideshow": { 623 | "slide_type": "slide" 624 | } 625 | }, 626 | "outputs": [ 627 | { 628 | "name": "stdout", 629 | "output_type": "stream", 630 | "text": [ 631 | "A: Booting up system...\n", 632 | "B: Booting up system...\n", 633 | "A: Checking network connection\n", 634 | "B: Server up and running\n", 635 | ".........A: Request complex computation\n", 636 | "B: Starting heavy computation\n", 637 | "......................................A: Computation returned 42\n" 638 | ] 639 | } 640 | ], 641 | "source": [ 642 | "var { all } = Promise\n", 643 | "\n", 644 | "asyncProgress(main)\n", 645 | "\n", 646 | "async function main () {\n", 647 | " var [serverA, serverB] = await all([getServerA(), getServerB()])\n", 648 | " await serverA.sendRequest(serverB)\n", 649 | "}" 650 | ] 651 | }, 652 | { 653 | "cell_type": "markdown", 654 | "metadata": { 655 | "slideshow": { 656 | "slide_type": "slide" 657 | } 658 | }, 659 | "source": [ 660 | "# Reactive Extensions, as in CSharp\n", 661 | "\n", 662 | "After Reactive Extensions were introduced in C#, they were ported to several different programming environments, including JavaScript with Rx.js. So, we are going to explain the concept, showing a simple code example, and how it's possible to use the Rx.js API to manipulate and merge all sorts of different streams." 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": 18, 668 | "metadata": { 669 | "slideshow": { 670 | "slide_type": "slide" 671 | } 672 | }, 673 | "outputs": [], 674 | "source": [ 675 | "function getServerA(bootTime) {\n", 676 | " console.log(\"A: Booting up system...\")\n", 677 | "\n", 678 | " return Promise.delay(bootTime)\n", 679 | " .then(checkNetwork)\n", 680 | " .then(() => ({ sendRequest }))\n", 681 | "\n", 682 | " function checkNetwork() {\n", 683 | " console.log(\"A: Checking network connection\")\n", 684 | " return delay(500)\n", 685 | " }\n", 686 | "\n", 687 | " function sendRequest(remoteServer) {\n", 688 | " console.log(\"A: Request complex computation\")\n", 689 | "\n", 690 | " return remoteServer.compute(2)\n", 691 | " .do(value => console.log(`A: Received value ${value}`))\n", 692 | " .take(10)\n", 693 | " .reduce((acc, value) => acc + value, 0)\n", 694 | " .toPromise()\n", 695 | " .then(total => console.log(`A: Computation returned ${total}`))\n", 696 | " }\n", 697 | "}" 698 | ] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "execution_count": 19, 703 | "metadata": { 704 | "slideshow": { 705 | "slide_type": "slide" 706 | } 707 | }, 708 | "outputs": [], 709 | "source": [ 710 | "function getServerB(bootTime) {\n", 711 | " console.log(\"B: Booting up system...\")\n", 712 | "\n", 713 | " return Promise.delay(bootTime).then(() => {\n", 714 | " console.log(\"B: Server up and running\")\n", 715 | " return { compute }\n", 716 | " })\n", 717 | "\n", 718 | " function compute(value) {\n", 719 | " console.log(\"B: Starting heavy computation\")\n", 720 | " return Observable.interval(200)\n", 721 | " .scan((acc, x, i) => acc + i * 2, value)\n", 722 | " }\n", 723 | "}" 724 | ] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": 20, 729 | "metadata": { 730 | "slideshow": { 731 | "slide_type": "slide" 732 | } 733 | }, 734 | "outputs": [ 735 | { 736 | "name": "stdout", 737 | "output_type": "stream", 738 | "text": [ 739 | "A: Booting up system...\n", 740 | "B: Booting up system...\n", 741 | ".B: Server up and running\n", 742 | ".................A: Checking network connection\n", 743 | "..........A: Request complex computation\n", 744 | "B: Starting heavy computation\n", 745 | "....A: Received value 2\n", 746 | "....A: Received value 4\n", 747 | "...A: Received value 8\n", 748 | "....A: Received value 14\n", 749 | "....A: Received value 22\n", 750 | "....A: Received value 32\n", 751 | "....A: Received value 44\n", 752 | "....A: Received value 58\n", 753 | "....A: Received value 74\n", 754 | "...A: Received value 92\n", 755 | "A: Computation returned 350\n" 756 | ] 757 | } 758 | ], 759 | "source": [ 760 | "var { Observable } = require('rxjs')\n", 761 | "\n", 762 | "asyncProgress(main)\n", 763 | "\n", 764 | "function main () {\n", 765 | " return Promise.props({\n", 766 | " serverA: getServerA(1000),\n", 767 | " serverB: getServerB(100)\n", 768 | " }).then(({ serverA, serverB }) => {\n", 769 | " return serverA.sendRequest(serverB)\n", 770 | " })\n", 771 | "}" 772 | ] 773 | }, 774 | { 775 | "cell_type": "markdown", 776 | "metadata": { 777 | "slideshow": { 778 | "slide_type": "slide" 779 | } 780 | }, 781 | "source": [ 782 | "# Channel-based programming, as in Go\n", 783 | "\n", 784 | "Go leverages Communicating Sequential Processes (aka CSP), by using blocking channels to coordinate the communication of many goroutines. Even though JavaScript is single threaded, the library `js-csp` uses generators the same style of programming. We are going to show an example of how use it to manipulate streams of async events in an imperative manner. " 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": 28, 790 | "metadata": { 791 | "slideshow": { 792 | "slide_type": "slide" 793 | } 794 | }, 795 | "outputs": [], 796 | "source": [ 797 | "var { go, put, take, CLOSED, timeout, chan } = require('js-csp')\n", 798 | "var toPromise = channel => {\n", 799 | " return new Promise(resolve =>\n", 800 | " go(function * () {\n", 801 | " resolve(yield take(channel))\n", 802 | " })\n", 803 | " )\n", 804 | "}" 805 | ] 806 | }, 807 | { 808 | "cell_type": "code", 809 | "execution_count": 29, 810 | "metadata": { 811 | "slideshow": { 812 | "slide_type": "slide" 813 | } 814 | }, 815 | "outputs": [], 816 | "source": [ 817 | "async function getServerA(bootTime) {\n", 818 | " console.log(\"A: Booting up system...\")\n", 819 | " await delay(bootTime)\n", 820 | " await checkNetwork()\n", 821 | "\n", 822 | " return { sendRequest }\n", 823 | "\n", 824 | " function checkNetwork() {\n", 825 | " console.log(\"A: Checking network connection\")\n", 826 | " return delay(500)\n", 827 | " }\n", 828 | "\n", 829 | " async function sendRequest(remoteServer) {\n", 830 | " console.log(\"A: Request complex computation\")\n", 831 | " var channel = remoteServer.compute(2)\n", 832 | "\n", 833 | " var value = await toPromise(csp.go(function * () {\n", 834 | " var acc = 0\n", 835 | " var count = 0\n", 836 | " while (true) {\n", 837 | " var value = yield take(channel)\n", 838 | "\n", 839 | " console.log(`A: Received ${value}`)\n", 840 | "\n", 841 | " acc += value\n", 842 | " count += 1\n", 843 | "\n", 844 | " if (count > 10) {\n", 845 | " channel.close()\n", 846 | " return acc\n", 847 | " }\n", 848 | " }\n", 849 | " }))\n", 850 | "\n", 851 | " console.log(`A: Computation returned ${value}`)\n", 852 | " }\n", 853 | "}" 854 | ] 855 | }, 856 | { 857 | "cell_type": "code", 858 | "execution_count": 30, 859 | "metadata": { 860 | "slideshow": { 861 | "slide_type": "slide" 862 | } 863 | }, 864 | "outputs": [], 865 | "source": [ 866 | "async function getServerB(bootTime) {\n", 867 | " console.log(\"B: Booting up system...\")\n", 868 | " await delay(bootTime)\n", 869 | " console.log(\"B: Server up and running\")\n", 870 | " return { compute }\n", 871 | "\n", 872 | " function compute(value) {\n", 873 | " console.log(\"B: Starting heavy computation\")\n", 874 | "\n", 875 | " var channel = csp.chan()\n", 876 | "\n", 877 | " go(function * () {\n", 878 | " var current = value\n", 879 | " var i = 1\n", 880 | "\n", 881 | " while (yield put(channel, current)) {\n", 882 | " current += i * 2\n", 883 | " i++\n", 884 | "\n", 885 | " yield timeout(200)\n", 886 | " }\n", 887 | " })\n", 888 | "\n", 889 | " return channel\n", 890 | " }\n", 891 | "}" 892 | ] 893 | }, 894 | { 895 | "cell_type": "code", 896 | "execution_count": 31, 897 | "metadata": { 898 | "slideshow": { 899 | "slide_type": "slide" 900 | } 901 | }, 902 | "outputs": [ 903 | { 904 | "name": "stdout", 905 | "output_type": "stream", 906 | "text": [ 907 | "A: Booting up system...\n", 908 | "B: Booting up system...\n", 909 | "A: Checking network connection\n", 910 | "B: Server up and running\n", 911 | ".........A: Request complex computation\n", 912 | "B: Starting heavy computation\n", 913 | "A: Received 2\n", 914 | "....A: Received 4\n", 915 | "....A: Received 8\n", 916 | "...A: Received 14\n", 917 | "....A: Received 22\n", 918 | "....A: Received 32\n", 919 | "....A: Received 44\n", 920 | "....A: Received 58\n", 921 | "....A: Received 74\n", 922 | "...A: Received 92\n", 923 | "....A: Received 112\n", 924 | "A: Computation returned 462\n" 925 | ] 926 | } 927 | ], 928 | "source": [ 929 | "asyncProgress(main)\n", 930 | "\n", 931 | "async function main () {\n", 932 | " var [serverA, serverB] = await all([getServerA(), getServerB()])\n", 933 | " await serverA.sendRequest(serverB)\n", 934 | "}" 935 | ] 936 | }, 937 | { 938 | "cell_type": "code", 939 | "execution_count": 25, 940 | "metadata": { 941 | "slideshow": { 942 | "slide_type": "slide" 943 | } 944 | }, 945 | "outputs": [ 946 | { 947 | "ename": "SyntaxError", 948 | "evalue": "Unexpected token *", 949 | "output_type": "error", 950 | "traceback": [ 951 | "evalmachine.:3", 952 | "async function * producer() {", 953 | " ^", 954 | "", 955 | "SyntaxError: Unexpected token *", 956 | " at createScript (vm.js:74:10)", 957 | " at Object.runInThisContext (vm.js:116:10)", 958 | " at run ([eval]:757:15)", 959 | " at onRunRequest ([eval]:597:18)", 960 | " at [eval]:563:24", 961 | " at tryCatcher (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/util.js:16:23)", 962 | " at Promise._settlePromiseFromHandler (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:512:31)", 963 | " at Promise._settlePromise (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:569:18)", 964 | " at Promise._settlePromiseCtx (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:606:10)", 965 | " at Async._drainQueue (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:138:12)", 966 | " at Async._drainQueues (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:143:10)", 967 | " at Immediate.Async.drainQueues (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:17:14)", 968 | " at runCallback (timers.js:781:20)", 969 | " at tryOnImmediate (timers.js:743:5)", 970 | " at processImmediate [as _immediateCallback] (timers.js:714:5)" 971 | ] 972 | } 973 | ], 974 | "source": [ 975 | "var { delay } = require('bluebird')\n", 976 | "\n", 977 | "async function * producer() {\n", 978 | " const stream = [1, 2, 3, 4, 5]\n", 979 | " for (let val of stream) {\n", 980 | " await delay(1000)\n", 981 | " yield val\n", 982 | " }\n", 983 | "}\n", 984 | "\n", 985 | "async function consumer () {\n", 986 | " for await (let val of producer()) {\n", 987 | " console.log(val)\n", 988 | " }\n", 989 | "}\n", 990 | "\n", 991 | "consumer()" 992 | ] 993 | }, 994 | { 995 | "cell_type": "markdown", 996 | "metadata": { 997 | "slideshow": { 998 | "slide_type": "slide" 999 | } 1000 | }, 1001 | "source": [ 1002 | "# Actor-based programming, as in Erlang and Elixir.\n", 1003 | "\n", 1004 | "All of the previous async techniques don't solve JavaScripts limitation of running in a single thread. That can, however, be worked around using Actor based programming, just like in Erlang and Elixir. Actor based programming works with idea of Actors, which can be thought of asynchronous and independent objects. With that concept in mind, we use message passing to communicate with the actors. The big concept here is, because Actors are independent from each other, can run in different processes. Therefore, we can use a library like `comedy` to run our application across many machines or many processes within the same machine. We are going to demonstrate an example of how this can be accomplished." 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "markdown", 1009 | "metadata": { 1010 | "slideshow": { 1011 | "slide_type": "slide" 1012 | } 1013 | }, 1014 | "source": [ 1015 | "# Comparison of all different methods used\n", 1016 | "\n", 1017 | "```\n", 1018 | "+------------+-------------+------------------------------+\n", 1019 | "| | One Event | Multiple Events |\n", 1020 | "+------------+-------------+------------------------------+\n", 1021 | "| Functional | Callbacks | Reactive Extensions |\n", 1022 | "| | Promises | |\n", 1023 | "+------------+-------------+------------------------------+\n", 1024 | "| Imperative | Async/Await | Async Generators & Iterators |\n", 1025 | "| | | Go-like channels |\n", 1026 | "+------------+-------------+------------------------------+\n", 1027 | "```" 1028 | ] 1029 | } 1030 | ], 1031 | "metadata": { 1032 | "celltoolbar": "Slideshow", 1033 | "kernelspec": { 1034 | "display_name": "Javascript (Node.js)", 1035 | "language": "javascript", 1036 | "name": "javascript" 1037 | }, 1038 | "language_info": { 1039 | "file_extension": ".js", 1040 | "mimetype": "application/javascript", 1041 | "name": "javascript", 1042 | "version": "8.5.0" 1043 | } 1044 | }, 1045 | "nbformat": 4, 1046 | "nbformat_minor": 2 1047 | } 1048 | -------------------------------------------------------------------------------- /2017/theconf-expanding-async-in-javascript/.ipynb_checkpoints/slides-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "slideshow": { 7 | "slide_type": "slide" 8 | } 9 | }, 10 | "source": [ 11 | "# Beyond Promises\n", 12 | "\n", 13 | "## Async Patterns in JavaScript\n", 14 | "\n", 15 | "
\n", 16 | "
\n", 17 | "
\n", 18 | "\n", 19 | "
by Thales Mello
" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": { 25 | "slideshow": { 26 | "slide_type": "slide" 27 | } 28 | }, 29 | "source": [ 30 | "# Let's start with a story\n", 31 | "\n", 32 | "Weird JavaScript behavior had always kept me from sharing any interest with Node.\n", 33 | "\n", 34 | "But in 2015, I was helping friend build a chat microservice for his app\n", 35 | "\n", 36 | "The alternative we chose to solve the problem as a combination of [Node](https://nodejs.org/en/) + [SocketIO](https://socket.io/)..." 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": { 42 | "slideshow": { 43 | "slide_type": "slide" 44 | } 45 | }, 46 | "source": [ 47 | "and it I became really impressed with the final results!\n", 48 | "\n", 49 | "But frankly, the programming environment felt very awkward.\n", 50 | "\n", 51 | "Anyhow, it was enough to stir up my interest about asynchronous programming." 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": { 57 | "slideshow": { 58 | "slide_type": "slide" 59 | } 60 | }, 61 | "source": [ 62 | "# What exectly do you mean with awkward programming environment?\n", 63 | "\n", 64 | "Callbacks." 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": { 70 | "slideshow": { 71 | "slide_type": "slide" 72 | } 73 | }, 74 | "source": [ 75 | "In Node, callbacks are the builtin way of invoking asynchronous code, and in the first years of Node, it was the only way.\n", 76 | "\n", 77 | "And because asynchronous programming was the whole point of using Node, I frankly used callbacks for everything." 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": { 83 | "slideshow": { 84 | "slide_type": "slide" 85 | } 86 | }, 87 | "source": [ 88 | "# Node\n", 89 | "\n", 90 | "- Allows us to create fast servers relying on JavaScript's event driven approach to programming\n", 91 | "- But working with async programming can be hard" 92 | ] 93 | }, 94 | { 95 | "cell_type": "markdown", 96 | "metadata": { 97 | "slideshow": { 98 | "slide_type": "slide" 99 | } 100 | }, 101 | "source": [ 102 | "# How exactly callbacks work?" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 32, 108 | "metadata": { 109 | "slideshow": { 110 | "slide_type": "slide" 111 | } 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "var sleep = time => {\n", 116 | " let start = new Date().getTime()\n", 117 | " let expire = start + time;\n", 118 | "\n", 119 | " while (new Date().getTime() < expire);\n", 120 | "}" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 33, 126 | "metadata": { 127 | "slideshow": { 128 | "slide_type": "slide" 129 | } 130 | }, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "Hello\n", 137 | "world!\n" 138 | ] 139 | } 140 | ], 141 | "source": [ 142 | "// Consider this sync example. The 'world' is delayed when compared\n", 143 | "// to the first line. But it happens at the cost of blocking JavaScript's\n", 144 | "// single thread.\n", 145 | "console.log('Hello')\n", 146 | "sleep(1000)\n", 147 | "console.log('world!')" 148 | ] 149 | }, 150 | { 151 | "cell_type": "code", 152 | "execution_count": 34, 153 | "metadata": { 154 | "slideshow": { 155 | "slide_type": "slide" 156 | } 157 | }, 158 | "outputs": [ 159 | { 160 | "name": "stdout", 161 | "output_type": "stream", 162 | "text": [ 163 | "Hello\n", 164 | "world!\n" 165 | ] 166 | } 167 | ], 168 | "source": [ 169 | "var progress = fn => {\n", 170 | " var handle = setInterval(() => process.stdout.write('.'), 50)\n", 171 | " fn(() => clearTimeout(handle))\n", 172 | "}\n", 173 | "\n", 174 | "progress(done => {\n", 175 | " console.log('Hello')\n", 176 | " sleep(1000)\n", 177 | " console.log('world!')\n", 178 | " done()\n", 179 | "})" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 35, 185 | "metadata": { 186 | "scrolled": true, 187 | "slideshow": { 188 | "slide_type": "slide" 189 | } 190 | }, 191 | "outputs": [ 192 | { 193 | "name": "stdout", 194 | "output_type": "stream", 195 | "text": [ 196 | "Hello\n", 197 | "..................world!\n" 198 | ] 199 | } 200 | ], 201 | "source": [ 202 | "var timeout = (time, fn) => setTimeout(fn, time)\n", 203 | "\n", 204 | "progress(done => {\n", 205 | " console.log('Hello')\n", 206 | " timeout(1000, () => {\n", 207 | " console.log('world!')\n", 208 | " done()\n", 209 | " })\n", 210 | "})" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": { 216 | "slideshow": { 217 | "slide_type": "slide" 218 | } 219 | }, 220 | "source": [ 221 | "# Let's work with a two servers example " 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 39, 227 | "metadata": { 228 | "slideshow": { 229 | "slide_type": "slide" 230 | } 231 | }, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "A: Booting up system\n", 238 | "B: Booting up system\n", 239 | ".B: Server up and running\n", 240 | "..................A: Checking network connection\n", 241 | ".........A: Request complex computation\n", 242 | "B: Starting heavy computation\n", 243 | "......................................A: Computation returned 42\n" 244 | ] 245 | } 246 | ], 247 | "source": [ 248 | "progress(done => {\n", 249 | " var queueCallback, serverHandler\n", 250 | " console.log(\"A: Booting up system\")\n", 251 | " timeout(1000, () => {\n", 252 | " console.log(\"A: Checking network connection\")\n", 253 | " timeout(500, () => {\n", 254 | " console.log(\"A: Request complex computation\")\n", 255 | " sendRequest(value => {\n", 256 | " console.log(\"A: Computation returned \" + value)\n", 257 | " done()\n", 258 | " })\n", 259 | " })\n", 260 | " })\n", 261 | "\n", 262 | " console.log(\"B: Booting up system\")\n", 263 | " timeout(100, () => {\n", 264 | " console.log(\"B: Server up and running\")\n", 265 | " serverHandler = (callback) => {\n", 266 | " console.log(\"B: Starting heavy computation\")\n", 267 | " timeout(2000, () => callback(42))\n", 268 | " }\n", 269 | " if (queueCallback) { serverHandler(queueCallback) }\n", 270 | " })\n", 271 | "\n", 272 | " function sendRequest(callback) {\n", 273 | " if (serverHandler) { serverHandler(callback) }\n", 274 | " else { queueCallback = callback }\n", 275 | " }\n", 276 | "})" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 40, 282 | "metadata": { 283 | "collapsed": true, 284 | "slideshow": { 285 | "slide_type": "slide" 286 | } 287 | }, 288 | "outputs": [], 289 | "source": [ 290 | "function serverA(bootTime, done) {\n", 291 | " console.log(\"A: Booting up system...\")\n", 292 | " timeout(bootTime, checkNetwork)\n", 293 | "\n", 294 | " function checkNetwork() {\n", 295 | " console.log(\"A: Checking network connection\")\n", 296 | " timeout(500, sendRequest)\n", 297 | " }\n", 298 | "\n", 299 | " function sendRequest() {\n", 300 | " console.log(\"A: Request complex computation\")\n", 301 | " sendNetworkRequest(callback)\n", 302 | " }\n", 303 | "\n", 304 | " function callback(value) {\n", 305 | " console.log(\"A: Computation returned \" + value)\n", 306 | " done()\n", 307 | " }\n", 308 | "}" 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "execution_count": 41, 314 | "metadata": { 315 | "collapsed": true, 316 | "slideshow": { 317 | "slide_type": "slide" 318 | } 319 | }, 320 | "outputs": [], 321 | "source": [ 322 | "function serverB(bootTime) {\n", 323 | " console.log(\"B: Booting up system...\")\n", 324 | " timeout(bootTime, listenRequests)\n", 325 | "\n", 326 | " function listenRequests() {\n", 327 | " console.log(\"B: Server up and running\")\n", 328 | " serverHandler = handler\n", 329 | "\n", 330 | " if (queueCallback) {\n", 331 | " serverHandler(queueCallback)\n", 332 | " queueCallback = null\n", 333 | " }\n", 334 | " }\n", 335 | "\n", 336 | " function handler(callback) {\n", 337 | " console.log(\"B: Starting heavy computation\")\n", 338 | " timeout(2000, () => callback(42))\n", 339 | " }\n", 340 | "}" 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": 9, 346 | "metadata": { 347 | "slideshow": { 348 | "slide_type": "slide" 349 | } 350 | }, 351 | "outputs": [ 352 | { 353 | "name": "stdout", 354 | "output_type": "stream", 355 | "text": [ 356 | "A: Booting up system...\n", 357 | "B: Booting up system...\n", 358 | ".B: Server up and running\n", 359 | ".................A: Checking network connection\n", 360 | "..........A: Request complex computation\n", 361 | "B: Starting heavy computation\n", 362 | "......................................A: Computation returned 42\n" 363 | ] 364 | } 365 | ], 366 | "source": [ 367 | "function sendNetworkRequest(callback) {\n", 368 | " if(serverHandler) {\n", 369 | " serverHandler(callback)\n", 370 | " } else {\n", 371 | " queueCallback = callback\n", 372 | " }\n", 373 | "}\n", 374 | "\n", 375 | "var queueCallback = null\n", 376 | "var serverHandler = null\n", 377 | "\n", 378 | "progress((done) => {\n", 379 | " serverA(1000, done)\n", 380 | " serverB(100)\n", 381 | "})" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": { 387 | "slideshow": { 388 | "slide_type": "slide" 389 | } 390 | }, 391 | "source": [ 392 | "# Wait, have you heard about promises?\n", 393 | "\n", 394 | "A quick Promises explanation, according to the Promises/A+ specification, for people not familiar with the concept. We are going to explain how we return an object that will be resolved or will fail in the future, and how actions can be chained with the `.then()` method." 395 | ] 396 | }, 397 | { 398 | "cell_type": "code", 399 | "execution_count": 10, 400 | "metadata": { 401 | "slideshow": { 402 | "slide_type": "slide" 403 | } 404 | }, 405 | "outputs": [ 406 | { 407 | "name": "stdout", 408 | "output_type": "stream", 409 | "text": [ 410 | "..................Hello world!\n", 411 | ".........." 412 | ] 413 | }, 414 | { 415 | "data": { 416 | "text/plain": [ 417 | "'I will be back'" 418 | ] 419 | }, 420 | "execution_count": 10, 421 | "metadata": {}, 422 | "output_type": "execute_result" 423 | } 424 | ], 425 | "source": [ 426 | "var { promisify } = require('util')\n", 427 | "var delay = promisify(setTimeout)\n", 428 | "var asyncProgress = fn => {\n", 429 | " var handle = setInterval(() => process.stdout.write('.'), 50)\n", 430 | " return new Promise((resolve, reject) => {\n", 431 | " fn().then(value => {\n", 432 | " clearTimeout(handle)\n", 433 | " resolve(value)\n", 434 | " }).catch(error => {\n", 435 | " clearTimeout(handle)\n", 436 | " reject(error)\n", 437 | " })\n", 438 | " })\n", 439 | "}\n", 440 | "\n", 441 | "asyncProgress(() => {\n", 442 | " return delay(1000)\n", 443 | " .then(() => {\n", 444 | " console.log('Hello world!')\n", 445 | " return \"I will be back\"\n", 446 | " })\n", 447 | " .then(value => delay(500).then(() => value))\n", 448 | "})\n" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 11, 454 | "metadata": { 455 | "collapsed": true, 456 | "slideshow": { 457 | "slide_type": "slide" 458 | } 459 | }, 460 | "outputs": [], 461 | "source": [ 462 | "function getServerA(bootTime) {\n", 463 | " console.log(\"A: Booting up system...\")\n", 464 | "\n", 465 | " return Promise.delay(bootTime)\n", 466 | " .then(checkNetwork)\n", 467 | " .then(() => ({ sendRequest }))\n", 468 | "\n", 469 | " function checkNetwork() {\n", 470 | " console.log(\"A: Checking network connection\")\n", 471 | " return delay(500)\n", 472 | " }\n", 473 | "\n", 474 | " function sendRequest(remoteServer) {\n", 475 | " console.log(\"A: Request complex computation\")\n", 476 | " return remoteServer\n", 477 | " .compute(2)\n", 478 | " .then(value => {\n", 479 | " console.log\n", 480 | " console.log(`A: Computation returned ${value}`)\n", 481 | " })\n", 482 | " }\n", 483 | "}" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 12, 489 | "metadata": { 490 | "slideshow": { 491 | "slide_type": "slide" 492 | } 493 | }, 494 | "outputs": [], 495 | "source": [ 496 | "function getServerB(bootTime) {\n", 497 | " console.log(\"B: Booting up system...\")\n", 498 | "\n", 499 | " return Promise.delay(bootTime).then(() => {\n", 500 | " console.log(\"B: Server up and running\")\n", 501 | " return { compute }\n", 502 | " })\n", 503 | "\n", 504 | " function compute(value) {\n", 505 | " console.log(\"B: Starting heavy computation\")\n", 506 | " return delay(2000).then(() => 40 + value)\n", 507 | " }\n", 508 | "}" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": 13, 514 | "metadata": { 515 | "slideshow": { 516 | "slide_type": "slide" 517 | } 518 | }, 519 | "outputs": [ 520 | { 521 | "name": "stdout", 522 | "output_type": "stream", 523 | "text": [ 524 | "A: Booting up system...\n", 525 | "B: Booting up system...\n", 526 | ".B: Server up and running\n", 527 | "..................A: Checking network connection\n", 528 | ".........A: Request complex computation\n", 529 | "B: Starting heavy computation\n", 530 | "......................................A: Computation returned 42\n" 531 | ] 532 | } 533 | ], 534 | "source": [ 535 | "var Promise = require('bluebird')\n", 536 | "\n", 537 | "asyncProgress(main)\n", 538 | "\n", 539 | "function main () {\n", 540 | " return Promise.props({\n", 541 | " serverA: getServerA(1000),\n", 542 | " serverB: getServerB(100)\n", 543 | " }).then(({ serverA, serverB }) => {\n", 544 | " return serverA.sendRequest(serverB)\n", 545 | " })\n", 546 | "}" 547 | ] 548 | }, 549 | { 550 | "cell_type": "markdown", 551 | "metadata": { 552 | "slideshow": { 553 | "slide_type": "slide" 554 | } 555 | }, 556 | "source": [ 557 | "# Async/Await as an imperative, easier to read approach\n", 558 | "\n", 559 | "Promises end up being a more functional programming approach, but a little more difficult to read for folks used to imperative programming. After function generators (aka coroutines) were introduced in the ES2015 specification, people started using it to replicate the Async/Await feature from C#, to be able to await Promises. We are going to see how it was used, and how it evolved to be officially adopted in the ES2017 specification.\n", 560 | "\n", 561 | "We are also going to talk about Async/Await limitation of awaiting a single Promise at a time, and how other approaches might solve this problem." 562 | ] 563 | }, 564 | { 565 | "cell_type": "code", 566 | "execution_count": 14, 567 | "metadata": { 568 | "slideshow": { 569 | "slide_type": "slide" 570 | } 571 | }, 572 | "outputs": [], 573 | "source": [ 574 | "async function getServerA(bootTime) {\n", 575 | " console.log(\"A: Booting up system...\")\n", 576 | " await delay(bootTime)\n", 577 | " await checkNetwork()\n", 578 | "\n", 579 | " return { sendRequest }\n", 580 | "\n", 581 | " function checkNetwork() {\n", 582 | " console.log(\"A: Checking network connection\")\n", 583 | " return delay(500)\n", 584 | " }\n", 585 | "\n", 586 | " async function sendRequest(remoteServer) {\n", 587 | " console.log(\"A: Request complex computation\")\n", 588 | " var value = await remoteServer.compute(2)\n", 589 | " console.log(`A: Computation returned ${value}`)\n", 590 | " }\n", 591 | "}" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 15, 597 | "metadata": { 598 | "slideshow": { 599 | "slide_type": "slide" 600 | } 601 | }, 602 | "outputs": [], 603 | "source": [ 604 | "async function getServerB(bootTime) {\n", 605 | " console.log(\"B: Booting up system...\")\n", 606 | " await delay(bootTime)\n", 607 | " console.log(\"B: Server up and running\")\n", 608 | " return { compute }\n", 609 | "\n", 610 | " async function compute(value) {\n", 611 | " console.log(\"B: Starting heavy computation\")\n", 612 | " await delay(2000)\n", 613 | " return 40 + value\n", 614 | " }\n", 615 | "}" 616 | ] 617 | }, 618 | { 619 | "cell_type": "code", 620 | "execution_count": 16, 621 | "metadata": { 622 | "slideshow": { 623 | "slide_type": "slide" 624 | } 625 | }, 626 | "outputs": [ 627 | { 628 | "name": "stdout", 629 | "output_type": "stream", 630 | "text": [ 631 | "A: Booting up system...\n", 632 | "B: Booting up system...\n", 633 | "A: Checking network connection\n", 634 | "B: Server up and running\n", 635 | ".........A: Request complex computation\n", 636 | "B: Starting heavy computation\n", 637 | "......................................A: Computation returned 42\n" 638 | ] 639 | } 640 | ], 641 | "source": [ 642 | "var { all } = Promise\n", 643 | "\n", 644 | "asyncProgress(main)\n", 645 | "\n", 646 | "async function main () {\n", 647 | " var [serverA, serverB] = await all([getServerA(), getServerB()])\n", 648 | " await serverA.sendRequest(serverB)\n", 649 | "}" 650 | ] 651 | }, 652 | { 653 | "cell_type": "markdown", 654 | "metadata": { 655 | "slideshow": { 656 | "slide_type": "slide" 657 | } 658 | }, 659 | "source": [ 660 | "# Reactive Extensions, as in CSharp\n", 661 | "\n", 662 | "After Reactive Extensions were introduced in C#, they were ported to several different programming environments, including JavaScript with Rx.js. So, we are going to explain the concept, showing a simple code example, and how it's possible to use the Rx.js API to manipulate and merge all sorts of different streams." 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": 18, 668 | "metadata": { 669 | "slideshow": { 670 | "slide_type": "slide" 671 | } 672 | }, 673 | "outputs": [], 674 | "source": [ 675 | "function getServerA(bootTime) {\n", 676 | " console.log(\"A: Booting up system...\")\n", 677 | "\n", 678 | " return Promise.delay(bootTime)\n", 679 | " .then(checkNetwork)\n", 680 | " .then(() => ({ sendRequest }))\n", 681 | "\n", 682 | " function checkNetwork() {\n", 683 | " console.log(\"A: Checking network connection\")\n", 684 | " return delay(500)\n", 685 | " }\n", 686 | "\n", 687 | " function sendRequest(remoteServer) {\n", 688 | " console.log(\"A: Request complex computation\")\n", 689 | "\n", 690 | " return remoteServer.compute(2)\n", 691 | " .do(value => console.log(`A: Received value ${value}`))\n", 692 | " .take(10)\n", 693 | " .reduce((acc, value) => acc + value, 0)\n", 694 | " .toPromise()\n", 695 | " .then(total => console.log(`A: Computation returned ${total}`))\n", 696 | " }\n", 697 | "}" 698 | ] 699 | }, 700 | { 701 | "cell_type": "code", 702 | "execution_count": 19, 703 | "metadata": { 704 | "slideshow": { 705 | "slide_type": "slide" 706 | } 707 | }, 708 | "outputs": [], 709 | "source": [ 710 | "function getServerB(bootTime) {\n", 711 | " console.log(\"B: Booting up system...\")\n", 712 | "\n", 713 | " return Promise.delay(bootTime).then(() => {\n", 714 | " console.log(\"B: Server up and running\")\n", 715 | " return { compute }\n", 716 | " })\n", 717 | "\n", 718 | " function compute(value) {\n", 719 | " console.log(\"B: Starting heavy computation\")\n", 720 | " return Observable.interval(200)\n", 721 | " .scan((acc, x, i) => acc + i * 2, value)\n", 722 | " }\n", 723 | "}" 724 | ] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": 20, 729 | "metadata": { 730 | "slideshow": { 731 | "slide_type": "slide" 732 | } 733 | }, 734 | "outputs": [ 735 | { 736 | "name": "stdout", 737 | "output_type": "stream", 738 | "text": [ 739 | "A: Booting up system...\n", 740 | "B: Booting up system...\n", 741 | ".B: Server up and running\n", 742 | ".................A: Checking network connection\n", 743 | "..........A: Request complex computation\n", 744 | "B: Starting heavy computation\n", 745 | "....A: Received value 2\n", 746 | "....A: Received value 4\n", 747 | "...A: Received value 8\n", 748 | "....A: Received value 14\n", 749 | "....A: Received value 22\n", 750 | "....A: Received value 32\n", 751 | "....A: Received value 44\n", 752 | "....A: Received value 58\n", 753 | "....A: Received value 74\n", 754 | "...A: Received value 92\n", 755 | "A: Computation returned 350\n" 756 | ] 757 | } 758 | ], 759 | "source": [ 760 | "var { Observable } = require('rxjs')\n", 761 | "\n", 762 | "asyncProgress(main)\n", 763 | "\n", 764 | "function main () {\n", 765 | " return Promise.props({\n", 766 | " serverA: getServerA(1000),\n", 767 | " serverB: getServerB(100)\n", 768 | " }).then(({ serverA, serverB }) => {\n", 769 | " return serverA.sendRequest(serverB)\n", 770 | " })\n", 771 | "}" 772 | ] 773 | }, 774 | { 775 | "cell_type": "markdown", 776 | "metadata": { 777 | "slideshow": { 778 | "slide_type": "slide" 779 | } 780 | }, 781 | "source": [ 782 | "# Channel-based programming, as in Go\n", 783 | "\n", 784 | "Go leverages Communicating Sequential Processes (aka CSP), by using blocking channels to coordinate the communication of many goroutines. Even though JavaScript is single threaded, the library `js-csp` uses generators the same style of programming. We are going to show an example of how use it to manipulate streams of async events in an imperative manner. " 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": 28, 790 | "metadata": { 791 | "slideshow": { 792 | "slide_type": "slide" 793 | } 794 | }, 795 | "outputs": [], 796 | "source": [ 797 | "var { go, put, take, CLOSED, timeout, chan } = require('js-csp')\n", 798 | "var toPromise = channel => {\n", 799 | " return new Promise(resolve =>\n", 800 | " go(function * () {\n", 801 | " resolve(yield take(channel))\n", 802 | " })\n", 803 | " )\n", 804 | "}" 805 | ] 806 | }, 807 | { 808 | "cell_type": "code", 809 | "execution_count": 29, 810 | "metadata": { 811 | "slideshow": { 812 | "slide_type": "slide" 813 | } 814 | }, 815 | "outputs": [], 816 | "source": [ 817 | "async function getServerA(bootTime) {\n", 818 | " console.log(\"A: Booting up system...\")\n", 819 | " await delay(bootTime)\n", 820 | " await checkNetwork()\n", 821 | "\n", 822 | " return { sendRequest }\n", 823 | "\n", 824 | " function checkNetwork() {\n", 825 | " console.log(\"A: Checking network connection\")\n", 826 | " return delay(500)\n", 827 | " }\n", 828 | "\n", 829 | " async function sendRequest(remoteServer) {\n", 830 | " console.log(\"A: Request complex computation\")\n", 831 | " var channel = remoteServer.compute(2)\n", 832 | "\n", 833 | " var value = await toPromise(csp.go(function * () {\n", 834 | " var acc = 0\n", 835 | " var count = 0\n", 836 | " while (true) {\n", 837 | " var value = yield take(channel)\n", 838 | "\n", 839 | " console.log(`A: Received ${value}`)\n", 840 | "\n", 841 | " acc += value\n", 842 | " count += 1\n", 843 | "\n", 844 | " if (count > 10) {\n", 845 | " channel.close()\n", 846 | " return acc\n", 847 | " }\n", 848 | " }\n", 849 | " }))\n", 850 | "\n", 851 | " console.log(`A: Computation returned ${value}`)\n", 852 | " }\n", 853 | "}" 854 | ] 855 | }, 856 | { 857 | "cell_type": "code", 858 | "execution_count": 30, 859 | "metadata": { 860 | "slideshow": { 861 | "slide_type": "slide" 862 | } 863 | }, 864 | "outputs": [], 865 | "source": [ 866 | "async function getServerB(bootTime) {\n", 867 | " console.log(\"B: Booting up system...\")\n", 868 | " await delay(bootTime)\n", 869 | " console.log(\"B: Server up and running\")\n", 870 | " return { compute }\n", 871 | "\n", 872 | " function compute(value) {\n", 873 | " console.log(\"B: Starting heavy computation\")\n", 874 | "\n", 875 | " var channel = csp.chan()\n", 876 | "\n", 877 | " go(function * () {\n", 878 | " var current = value\n", 879 | " var i = 1\n", 880 | "\n", 881 | " while (yield put(channel, current)) {\n", 882 | " current += i * 2\n", 883 | " i++\n", 884 | "\n", 885 | " yield timeout(200)\n", 886 | " }\n", 887 | " })\n", 888 | "\n", 889 | " return channel\n", 890 | " }\n", 891 | "}" 892 | ] 893 | }, 894 | { 895 | "cell_type": "code", 896 | "execution_count": 31, 897 | "metadata": { 898 | "slideshow": { 899 | "slide_type": "slide" 900 | } 901 | }, 902 | "outputs": [ 903 | { 904 | "name": "stdout", 905 | "output_type": "stream", 906 | "text": [ 907 | "A: Booting up system...\n", 908 | "B: Booting up system...\n", 909 | "A: Checking network connection\n", 910 | "B: Server up and running\n", 911 | ".........A: Request complex computation\n", 912 | "B: Starting heavy computation\n", 913 | "A: Received 2\n", 914 | "....A: Received 4\n", 915 | "....A: Received 8\n", 916 | "...A: Received 14\n", 917 | "....A: Received 22\n", 918 | "....A: Received 32\n", 919 | "....A: Received 44\n", 920 | "....A: Received 58\n", 921 | "....A: Received 74\n", 922 | "...A: Received 92\n", 923 | "....A: Received 112\n", 924 | "A: Computation returned 462\n" 925 | ] 926 | } 927 | ], 928 | "source": [ 929 | "asyncProgress(main)\n", 930 | "\n", 931 | "async function main () {\n", 932 | " var [serverA, serverB] = await all([getServerA(), getServerB()])\n", 933 | " await serverA.sendRequest(serverB)\n", 934 | "}" 935 | ] 936 | }, 937 | { 938 | "cell_type": "code", 939 | "execution_count": 25, 940 | "metadata": { 941 | "slideshow": { 942 | "slide_type": "slide" 943 | } 944 | }, 945 | "outputs": [ 946 | { 947 | "ename": "SyntaxError", 948 | "evalue": "Unexpected token *", 949 | "output_type": "error", 950 | "traceback": [ 951 | "evalmachine.:3", 952 | "async function * producer() {", 953 | " ^", 954 | "", 955 | "SyntaxError: Unexpected token *", 956 | " at createScript (vm.js:74:10)", 957 | " at Object.runInThisContext (vm.js:116:10)", 958 | " at run ([eval]:757:15)", 959 | " at onRunRequest ([eval]:597:18)", 960 | " at [eval]:563:24", 961 | " at tryCatcher (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/util.js:16:23)", 962 | " at Promise._settlePromiseFromHandler (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:512:31)", 963 | " at Promise._settlePromise (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:569:18)", 964 | " at Promise._settlePromiseCtx (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/promise.js:606:10)", 965 | " at Async._drainQueue (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:138:12)", 966 | " at Async._drainQueues (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:143:10)", 967 | " at Immediate.Async.drainQueues (/Users/thalesmello/projects/slides/2017/theconf-expanding-async-in-javascript/node_modules/bluebird/js/release/async.js:17:14)", 968 | " at runCallback (timers.js:781:20)", 969 | " at tryOnImmediate (timers.js:743:5)", 970 | " at processImmediate [as _immediateCallback] (timers.js:714:5)" 971 | ] 972 | } 973 | ], 974 | "source": [ 975 | "var { delay } = require('bluebird')\n", 976 | "\n", 977 | "async function * producer() {\n", 978 | " const stream = [1, 2, 3, 4, 5]\n", 979 | " for (let val of stream) {\n", 980 | " await delay(1000)\n", 981 | " yield val\n", 982 | " }\n", 983 | "}\n", 984 | "\n", 985 | "async function consumer () {\n", 986 | " for await (let val of producer()) {\n", 987 | " console.log(val)\n", 988 | " }\n", 989 | "}\n", 990 | "\n", 991 | "consumer()" 992 | ] 993 | }, 994 | { 995 | "cell_type": "markdown", 996 | "metadata": { 997 | "slideshow": { 998 | "slide_type": "slide" 999 | } 1000 | }, 1001 | "source": [ 1002 | "# Actor-based programming, as in Erlang and Elixir.\n", 1003 | "\n", 1004 | "All of the previous async techniques don't solve JavaScripts limitation of running in a single thread. That can, however, be worked around using Actor based programming, just like in Erlang and Elixir. Actor based programming works with idea of Actors, which can be thought of asynchronous and independent objects. With that concept in mind, we use message passing to communicate with the actors. The big concept here is, because Actors are independent from each other, can run in different processes. Therefore, we can use a library like `comedy` to run our application across many machines or many processes within the same machine. We are going to demonstrate an example of how this can be accomplished." 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "markdown", 1009 | "metadata": { 1010 | "slideshow": { 1011 | "slide_type": "slide" 1012 | } 1013 | }, 1014 | "source": [ 1015 | "# Comparison of all different methods used\n", 1016 | "\n", 1017 | "```\n", 1018 | "+------------+-------------+------------------------------+\n", 1019 | "| | One Event | Multiple Events |\n", 1020 | "+------------+-------------+------------------------------+\n", 1021 | "| Functional | Callbacks | Reactive Extensions |\n", 1022 | "| | Promises | |\n", 1023 | "+------------+-------------+------------------------------+\n", 1024 | "| Imperative | Async/Await | Async Generators & Iterators |\n", 1025 | "| | | Go-like channels |\n", 1026 | "+------------+-------------+------------------------------+\n", 1027 | "```" 1028 | ] 1029 | } 1030 | ], 1031 | "metadata": { 1032 | "celltoolbar": "Slideshow", 1033 | "kernelspec": { 1034 | "display_name": "Javascript (Node.js)", 1035 | "language": "javascript", 1036 | "name": "javascript" 1037 | }, 1038 | "language_info": { 1039 | "file_extension": ".js", 1040 | "mimetype": "application/javascript", 1041 | "name": "javascript", 1042 | "version": "8.5.0" 1043 | } 1044 | }, 1045 | "nbformat": 4, 1046 | "nbformat_minor": 2 1047 | } 1048 | --------------------------------------------------------------------------------