Share
Embed
Controle de fluxo com execução assíncrona
A teoria e os desafios por trás do controle de fluxo: Callbacks, Promises e Promises com Generators analisados de forma prática e crítica. De um lado a mais importante linguagem da Internet e seu ecossistema e do outro os principais desafios do desenvolvimento assíncrono.
Jean Carlo Emer
May 17, 2014
Transcript
137 slides
-
var size = 20; var
canvas = createCanvas(container); ! for (var edge = 0; edge < edges; edge++) { var angle = Math.PI * edge / (edges / 2); var range = createRange(centerRadius); range.addCanvas(canvas, edge); range.init(); } ! canvas.init(); Range.events(); -
if (condition); ! switch (expression) {} ! for
([before]; [condition]; [afterEach]); ! while (condition); ! for (variable in object); ! try {} catch (e) {} ... e funções -
A main-thread é compartilhada por código JavaScript, cálculos de CSS,
layout e painting. -
Contador externo que permite condicionar a execução de código para
o futuro. TIMERS setTimeout(callback, 3000); -
Executa o download de um arquivo e ser informado do
seu conteúdo no futuro. XML HTTP REQUEST var req = new XMLHttpRequest(); req.open('GET', url); req.onload = callback; -
Monitora eventos e executa código com base em uma interação
do usuário ou mudança de estado. DOM EVENTS el.addEventListener('click', callback); -
Monitora a árvore do DOM e executa código com base
em suas mudanças. MUTATION OBSERVER new MutationObserver(callback); -
Estabelecem uma conexão permanente com o servidor para envio e
recebimento de dados. WEB SOCKETS var ws = new WebSocket('ws://addr'); ws.onmessage = callback; -
Estabelecem uma conexão permanente com o servidor para recebimento de
dados. SERVER SENT EVENTS var source = new EventSource('addr'); source.onmessage = callback; -
Executam uma porção de código em uma thread separada da
principal. WEBWORKERS var worker = new Worker('addr.js'); worker.onmessage = callback; -
Código passado como argumento para que seja executado em um
tempo conveniente. CALLBACKS -
O interpretador endereça a execução das callbacks através de um
laço de eventos. -
As APIs emitem um evento para indicar que o código
da callback deve ser executado. -
Tudo é executado em paralelo, exceto o seu código. -
MIKITO TAKADA http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop -
SETTIMEOUT Define um timer nulo que tão logo agenda uma
tarefa na fila de eventos. ASSÍNCRONO COM * setTimeout(callback, 0); -
If nesting level is greater than 5, [...], then increase
timeout to 4. - WHATWG TIMER SPEC http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#timers -
SETIMMEDIATE Determina que uma tarefa seja colocada no fim da
fila do laço. ASSÍNCRONO COM adeus aos timers setImmediate(callback); https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html -
Suportada apenas no IE10* e node.js mas com alguns polyfills:
• postMessage e MessageChannel • <script> onreadystatechange • MutationObserver [*] IE10 bug - https://github.com/cujojs/when/issues/197 SETIMMEDIATE ASSÍNCRONO COM adeus aos timers -
WEBWORKERS Código executando num ambiente separado: worker. Resultados são passados
para o navegador através de callbacks. multithreads ASSÍNCRONO COM -
WEBWORKERS Para garantir ausência de concorrência, os workers não tem
acesso a página ou variáveis globais. Possuem acesso a XHR request, timers e a importação de scripts. multithreads ASSÍNCRONO COM -
this.fetch(data, function () { this.render(); }); X
Contexto do this -
this.fetch(data, () => { this.render(); }); use
arrow functions -
try { document.addEventListener('click', () => {
// ... throw new Error('o/'); }); } catch (error) { handleError(error); } X Tratamento de erros -
document.addEventListener('click', () => { // ...
handleError('o/'); }); aproveite as closures -
$.get('products', (products) => { products.forEach((product) => {
var element = $('<a>').text(product.name); element.on('click', (event) => { var url = 'products/' + product.id; $.get(url, (product) => { // ... }); }); // ... }); }); X Callback hell -
class ProductsFactory { fetch() {
$.get('products', this.onSync.bind(this)); } onSync(data) { products.forEach((item) => { var product = new Product(item).render(); // ... }); } } http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them -
class Product { constructor(data) {
this.data = data; this.element = $('<a>'); } render() { this.element.text(data.name); return this; } // ... } organize! http://javascriptissexy.com/understand-javascript-callback-functions-and-use-them -
$.get('products', function callback(data) { callback2(); callback3();
callback4(); }); alto acoplamento X -
var products; ! $.get('products', function (data) { products = data;
}); não há nada que uma global não resolva X -
$.get('gifts', function (data) { if ($.active === 0)
{ this_is_my_callback(); } }); ! ! $.get('products', function (data) { if ($.active === 0) { this_is_my_callback(); } }); número de requisições abertas X -
var products = get('products'); ! products.then(function onFulfilled() {
// succeeded }, function onRejected() { // error }); retorna uma promise -
var products = get('products'); ! products.then(function onFulfilled() {
// succeeded }, function onRejected() { // error }); quando ou se já tiver um valor... -
Promessas guardam estado e caso já resolvidas, a callback mesmo
assim será invocado. -
setTimeout(function () { products.then(function () {
// ... }); }, 9999); não tem como perder o bonde! -
var products = get('products'); ! var basket = new
Basket(products); var list = new ProductsList(products); argumento -
new Promise(function (resolve, reject) { if (Math.random()
> .5) { resolve('success'); } else { reject('fail'); } } -
new Promise(function (resolve, reject) { if (Math.random()
> .5) { resolve('success'); } else { reject('fail'); } } sucesso -
new Promise(function (resolve, reject) { if (Math.random()
> .5) { resolve('success'); } else { reject('fail'); } } falha -
function get(url) { return new Promise(function (resolve, reject)
{ var req = new XMLHttpRequest(); req.open('GET', url); ! req.onload = function () { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.send(); }); } http://www.html5rocks.com/en/tutorials/es6/promises -
function get(url) { return new Promise(function (resolve, reject)
{ var req = new XMLHttpRequest(); req.open('GET', url); ! req.onload = function () { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.send(); }); } http://www.html5rocks.com/en/tutorials/es6/promises operação assíncrona -
function get(url) { return new Promise(function (resolve, reject)
{ var req = new XMLHttpRequest(); req.open('GET', url); ! req.onload = function () { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.send(); }); } http://www.html5rocks.com/en/tutorials/es6/promises sucesso -
function get(url) { return new Promise(function (resolve, reject)
{ var req = new XMLHttpRequest(); req.open('GET', url); ! req.onload = function () { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.send(); }); } http://www.html5rocks.com/en/tutorials/es6/promises falha -
$.get('test.php').then( function() { alert('$.get
succeeded'); }, function() { alert('$.get failed!'); } ); jQuery entende* promises * aguarde, falaremos mais a respeito -
A popularização de chaining no JavaScript é devido em grande
parte a jQuery. -
$('[data-‐menu]') .css('opacity', 0) .fadeIn('fast')
.addClass('active') .remove(); chaining! -
parser.start() .then(parser.getFiles) .then(parser.generateIndex) .then(parser.generatePosts);
https://github.com/es6rocks/harmonic pega os arquivos -
parser.start() .then(parser.getFiles) .then(parser.generateIndex) .then(parser.generatePosts);
https://github.com/es6rocks/harmonic utiliza os arquivos -
parser.start() .then(function () { return
getFiles(); }) .then(function () { // ... }); -
parser.start() .then(function () { return
getFiles(); }) .then(function () { // ... }); espera por getFiles -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); cria uma promise resolvida -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); 1º BLOCO -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); ... resolvida -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); rejeita a promise -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); 2º BLOCO -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); resolvida? -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); "tratamento" -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); sucesso!! nenhum erro emitido -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); 3º BLOCO -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); resolvida? -
Promise.resolve('Yep') .then(function(data) { throw
new Error(data); }, function (error) { // nothing wrong with Yep }) .then(null, function (error) { console.error(error); }) .then(function () { console.log('I am fine'); }); termina aqui -
$.get('gifts', function (data) { if ($.active === 0)
{ this_is_my_callback(); } }); ! ! $.get('products', function (data) { if ($.active === 0) { this_is_my_callback(); } }); X -
Promise.all([ get('gifts'), get('products') ])
.then(function (results) { // results[0] -‐> gifts // results[1] -‐> products }, function (result) { // first rejected }); array de promises -
Promise.all([ get('gifts'), get('products') ])
.then(function (results) { // results[0] -‐> gifts // results[1] -‐> products }, function (result) { // first rejected }); todas resolvidas ou primeira rejeitada -
Promise.race([ get('gifts'), get('products') ])
.then(function (results) { // first resolved }, function (result) { // first rejected }); uma corrida?! -
Promise.race([ get('gifts'), get('products') ])
.then(function (results) { // first resolved }, function (result) { // or first rejected }); primeiro que terminar -
Nosso exemplos foram todos seguindo a especificação de Promises/A+. Algumas
implementações: • when, Q, RSVP, ... • ES6 Promises COMO USAR https://github.com/jakearchibald/es6-promise -
• Excessões nas callbacks quebram a execução de código •
Não permite chaining (1.8-) com then e sim com pipe. A IMPLEMENTAÇÃO e suas inconsistências DA JQUERY Promise.resolve( $.get('products')); -
function* infinite() { var i = 0;
while (true) { yield i++; } } ! var iterator = infinite(); iterator.next().value; iterator.next().value; indicativo de generator -
function* infinite() { var i = 0;
while (true) { yield i++; } } ! var iterator = infinite(); iterator.next().value; iterator.next().value; apenas inicia -
function* infinite() { var i = 0;
while (true) { yield i++; } } ! var iterator = infinite(); iterator.next().value; iterator.next().value; pega o i++ do yield -
function* infinite() { var i = 0;
while (true) { yield i++; } } ! var iterator = infinite(); iterator.next().value; iterator.next().value; executa o laço outra vez -
A cada resolução de uma promise, o next do generator
é executado. -
https://github.com/visionmedia/co co(function *(){ var products = yield get('products');
var gifts = yield get('gifts'); console.log(products[0]); console.log(gifts[0]); })(); -
co(function *(){ var products = yield get('products');
var gifts = yield get('gifts'); console.log(products[0]); console.log(gifts[0]); })(); https://github.com/visionmedia/co ao resolver a primeira... -
argumentos passados para o then https://github.com/visionmedia/co co(function *(){
var products = yield get('products'); var gifts = yield get('gifts'); console.log(products[0]); console.log(gifts[0]); })(); -
https://github.com/visionmedia/co co(function *(){ var getProducts = get('products');
var getGifts = get('gifts'); ! var products = yield getProducts; var gifts = yield getGifts; console.log(products[0]); console.log(gifts[0]); })(); paralelismo! -
[Promises] give you an abstraction that lets you model problems
at a higher level. - JAMES COGLAN https://blog.jcoglan.com/2013/03/30/callbacks-are-imperative- promises-are-functional-nodes-biggest-missed-opportunity -
Promises mean having enough memory to keep data around. -
DREW CRAWFORD http://sealedabstract.com/code/broken-promises Haters gonna hate. - 1º COMENTÁRIO