Controle de fluxo com execução assíncrona

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.

C5df370a883b65279af5a7ca94a5eed7?s=128

Jean Carlo Emer

May 17, 2014
Tweet

Transcript

  1. CONTROLE DE FLUXO com execução assíncrona

  2. http://jcemer.com

  3. Um dos organizadores RSJS FRONT IN POA http://frontinpoa.com.br http://rsjs.org

  4. Desenvolvedor front-end no GRUPO RBS http://gruporbs.com.br

  5. uma explicação breve FLUXO

  6. ORDEM DE EXECUÇÃO das INSTRUÇÕES

  7. 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();
  8. Algumas instruções são responsáveis por guiar o fluxo.

  9. if  (condition);   ! switch  (expression)  {}   ! for

     ([before];  [condition];  [afterEach]);   ! while  (condition);   ! for  (variable  in  object);   ! try  {}  catch  (e)  {} ... e funções
  10. Existe uma main-thread para
 cada aba do navegador.

  11. A main-thread é compartilhada por código JavaScript, cálculos de CSS,

    layout e painting.
  12. None
  13. ganhando aliados FLUXOS ASSÍNCRONOS

  14. Tarefas que executam em paralelo sem interferir no fluxo principal.

  15. Tarefas assíncronas geralmente regressam à main-thread no futuro.

  16. Contador externo que permite condicionar a execução de código para

    o futuro. TIMERS setTimeout(callback,  3000);
  17. 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;
  18. 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);
  19. Monitora a árvore do DOM e executa código com base

    em suas mudanças. MUTATION OBSERVER new  MutationObserver(callback);
  20. 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;
  21. Estabelecem uma conexão permanente com o servidor para recebimento de

    dados. SERVER SENT EVENTS var  source  =  new  EventSource('addr');   source.onmessage  =  callback;
  22. Executam uma porção de código em uma thread separada da

    principal. WEBWORKERS var  worker  =  new  Worker('addr.js');   worker.onmessage  =  callback;
  23. Envio de mensagens entre diferentes documentos. WEB MESSAGES window.addEventListener('message',  

                                                   callback);
  24. Todos exemplos de código possuíam algo em comum!

  25. $.get('products',  function  callback(data)  {      //...   });

  26. Código passado como argumento
 para que seja executado em um


    tempo conveniente. CALLBACKS
  27. O interpretador endereça a execução das callbacks através de um

    laço de eventos.
  28. As APIs emitem um evento para indicar que o código

    da callback deve ser executado.
  29. 1. DETECÇÃO DE EVENTOS 2. TRATAMENTO DE UM EVENTO 2

    1
  30. registra callback dispara callback OPERAÇÃO ASSÍNCRONA

  31. Tudo é executado em paralelo, exceto o seu código. -

    MIKITO TAKADA http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop
  32. razões e maneiras QUEBRANDO O FLUXO DE EXECUÇÃO

  33. ESCAPAR DOM EVENT BUBBLING do de um

  34. QUEBRAR LONGAS de TAREFAS EXECUÇÕES

  35. None
  36. EMULAR ASSÍNCRONO comportamento

  37. SETTIMEOUT Define um timer nulo que tão logo agenda uma

    tarefa na fila de eventos. ASSÍNCRONO COM * setTimeout(callback,  0);
  38. 5ª chamada

  39. 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
  40. menos de 1ms

  41. 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
  42. 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
  43. WEBWORKERS Código executando num ambiente separado: worker. Resultados são passados

    para o navegador através de callbacks. multithreads ASSÍNCRONO COM
  44. 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
  45. None
  46. Execução assíncrona no JavaScript é sobre código que utiliza callbacks.

  47. Não existe nenhum outro mecanismo na linguagem 
 além deste.

  48. None
  49. solucionando o hell CALLBACKS

  50. this.fetch(data,  function  ()  {      this.render();   }); X

    Contexto do this
  51. this.fetch(data,  ()  =>  {      this.render();   }); use

    arrow functions
  52. try  {      document.addEventListener('click',  ()  =>  {    

         //  ...          throw  new  Error('o/');      });   }  catch  (error)  {      handleError(error);   } X Tratamento de erros
  53. document.addEventListener('click',  ()  =>  {      //  ...    

     handleError('o/');   }); aproveite as closures
  54. $.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
  55. 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
  56. 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
  57. SIMPLES PODEROSAS e

  58. reais limitações CALLBACKS

  59. APENAS UM EVENTO por CALLBACK

  60. $.get('products',  function  callback(data)  {      callback2();      callback3();

         callback4();   }); alto acoplamento X
  61. ESTADO CALLBACKS NÃO MANTÊM

  62. var products; ! $.get('products', function (data) { products = data;

    }); não há nada que uma global não resolva X
  63. DIFÍCIL EVENTOS COORDENAR PARALELOS

  64. $.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
  65. uma alternativa PROMISES

  66. Programação funcional é sobre trabalhar com valores e não com

    funções.
  67. Uma promessa comumente representa o eventual resultado de uma operação

    assíncrona.
  68. var  products  =  get('products');   ! products.then(function  onFulfilled()  {  

       //  succeeded   },  function  onRejected()  {      //  error   }); retorna uma promise
  69. var  products  =  get('products');   ! products.then(function  onFulfilled()  {  

       //  succeeded   },  function  onRejected()  {      //  error   }); quando ou se já 
 tiver um valor...
  70. É possível adicionar vários listeners a uma promessa.

  71. products.then(callback1);   products.then(callback2);   products.then(callback3);

  72. Promessas guardam estado e caso já resolvidas, a callback mesmo

    assim será invocado.
  73. setTimeout(function  ()  {     products.then(function  ()  {    

       //  ...     });   },  9999); não tem como perder o bonde!
  74. Promessas são um valor que pode ser passado para outras

    funções.
  75. var  products  =  get('products');   ! var  basket  =  new

     Basket(products);   var  list      =  new  ProductsList(products); argumento
  76. .then

  77. o cativeiro CRIANDO SUAS PRÓPRIAS PROMISES

  78. new  Promise(function  (resolve,  reject)  {        if  (Math.random()

     >  .5)  {              resolve('success');        }  else  {              reject('fail');        }   }
  79. new  Promise(function  (resolve,  reject)  {        if  (Math.random()

     >  .5)  {              resolve('success');        }  else  {              reject('fail');        }   } sucesso
  80. new  Promise(function  (resolve,  reject)  {        if  (Math.random()

     >  .5)  {              resolve('success');        }  else  {              reject('fail');        }   } falha
  81. 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
  82. 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
  83. 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
  84. 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
  85. resolve() reject()

  86. Promise.promisify(libraryFunction); https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns aproveite todos
 os recursos Promisify

  87. $.get('test.php').then(      function()  {          alert('$.get

     succeeded');      },  function()  {          alert('$.get  failed!');      }   ); jQuery entende* promises * aguarde, falaremos mais a respeito
  88. encadeando promessas CHAINING

  89. A popularização de chaining no JavaScript é devido em grande

    parte a jQuery.
  90. $('[data-­‐menu]')      .css('opacity',  0)      .fadeIn('fast')    

     .addClass('active')      .remove(); chaining!
  91. parser.start()     .then(parser.getFiles)     .then(parser.generateIndex)     .then(parser.generatePosts);

    https://github.com/es6rocks/harmonic pega os arquivos
  92. parser.start()     .then(parser.getFiles)     .then(parser.generateIndex)     .then(parser.generatePosts);

    https://github.com/es6rocks/harmonic utiliza os
 arquivos
  93. O encadeamento é análogo a uma linha de montagem.

  94. parser.start()      .then(function  ()  {        return

     getFiles();     })     .then(function  ()  {       //  ...     });
  95. parser.start()      .then(function  ()  {        return

     getFiles();     })     .then(function  ()  {       //  ...     }); espera por getFiles
  96. like a boss TRATAMENTO DE ERRO

  97. 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
  98. 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
  99. 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
  100. 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
  101. 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
  102. 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?
  103. 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"
  104. 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
  105. 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
  106. 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?
  107. 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
  108. throw

  109. coordenando PARALELISMO

  110. $.get('gifts',  function  (data)  {      if  ($.active  ===  0)

     {            this_is_my_callback();      }   });   ! ! $.get('products',  function  (data)  {      if  ($.active  ===  0)  {            this_is_my_callback();      }   }); X
  111. Promise.all([      get('gifts'),        get('products')   ])

         .then(function  (results)  {          //  results[0]  -­‐>  gifts          //  results[1]  -­‐>  products      },  function  (result)  {          //  first  rejected      }); array de promises
  112. Promise.all([      get('gifts'),        get('products')   ])

         .then(function  (results)  {          //  results[0]  -­‐>  gifts          //  results[1]  -­‐>  products      },  function  (result)  {          //  first  rejected      }); todas resolvidas ou primeira 
 rejeitada
  113. Promise.race([      get('gifts'),        get('products')   ])

         .then(function  (results)  {          //  first  resolved      },  function  (result)  {          //  first  rejected      }); uma corrida?!
  114. Promise.race([      get('gifts'),        get('products')   ])

         .then(function  (results)  {          //  first  resolved      },  function  (result)  {          //  or  first  rejected      }); primeiro 
 que terminar
  115. 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
  116. • 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'));
  117. None
  118. um bônus GENERATORS

  119. Trata-se de uma função que constrói uma coleção 
 sob

    demanda.
  120. function*  infinite()  {      var  i  =  0;  

       while  (true)  {          yield  i++;      }   }   ! var  iterator  =  infinite();   iterator.next().value;   iterator.next().value; indicativo de generator
  121. function*  infinite()  {      var  i  =  0;  

       while  (true)  {          yield  i++;      }   }   ! var  iterator  =  infinite();   iterator.next().value;   iterator.next().value; apenas inicia
  122. function*  infinite()  {      var  i  =  0;  

       while  (true)  {          yield  i++;      }   }   ! var  iterator  =  infinite();   iterator.next().value;   iterator.next().value; pega o i++ 
 do yield
  123. function*  infinite()  {      var  i  =  0;  

       while  (true)  {          yield  i++;      }   }   ! var  iterator  =  infinite();   iterator.next().value;   iterator.next().value; executa o laço
 outra vez
  124. com GENERATORS PROMISES

  125. Abstração que encapsula um generator que retorna uma coleção de

    promises.
  126. A cada resolução de uma promise, o next do generator

    é executado.
  127. O generator deve criar e retorna uma próxima promise ou

    valor.
  128. 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]);   })();
  129. 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...
  130. 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]);   })();
  131. 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!
  132. None
  133. [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
  134. Promises mean having enough memory to keep data around. -

    DREW CRAWFORD http://sealedabstract.com/code/broken-promises Haters gonna hate. - 1º COMENTÁRIO
  135. Não existe bala de prata, cada caso tem suas particularidades.

  136. Callbacks e promises envolvem conceitos importantes que você deve conhecer.

  137. yo, rock 'n' roll OBRIGADO A TODOS