└── 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 | 
16 |
17 | ---
18 |
19 | E um pouco de Clojure (indiretamente)
20 |
21 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------