La programmation asynchrone... et les pates

2,083 views

Published on

La vidéo de la conférence au Forum PHP 2013 / AFUP : https://www.youtube.com/watch?v=WygFJHEloq0

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,083
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
11
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

La programmation asynchrone... et les pates

  1. 1. LA PROGRAMMATION ASYNCHRONE ET LES PÂTES François Zaninotto - @francoisz - marmelab.com
  2. 2. PARLONS CUISINE
  3. 3. Spaghetti à la tomate de Giuseppina Plat principal - Facile - Bon marché Pour 4 personnes Préparation et cuisson: 20 minutes Ingrédients: - 500g de spaghetti - 6 tomates bien mûres - 1 oignon - 1 carotte - 2 gousses d'ail - 1 branche de céleri - huile d'olive - sauge, romarin, basilic frais - sel et poivre - amour
  4. 4. Spaghetti à la tomate de Giuseppina Plat principal - Facile - Bon marché 1. Faire bouillir une grande quantité d'eau (non salée) dans une casserole 2. Pendant ce temps, pelez les tomates et coupez-les grossièrement 3. Epluchez et émincez l'oignon, la carotte, l'ail et le céleri 4. Lorsque l'eau bout, salez-la, puis déposez les spaghettis en couronne 5. Faire chauffer l'huile d'olive dans une sauteuse. Mettez-y à brunir la deuxième gousse d'ail préalablement épluchée. 6. Retirez la gousse d'ail, puis versez le mirepoix (mélange de légumes) et faites revenir à feu vif 7. Ajoutez les tomates, les herbes, salez et poivrez copieusement. Faites chauffer à feu moyen pendant 5 minutes 8. Goûtez régulièrement les spaghettis. Lorsqu'ils sont cuits al dente, égouttez-les puis déposez-les dans un plat chaud. 9. Versez la sauce tomate immédiatement sur les pâtes fumantes. Ajoutez le basilic grossièrement découpé. 10.Servez avec du parmesan rapé et un bon Chianti Classico, et régalez-vous
  5. 5. MAINTENANT LE CHEF C’EST PHP
  6. 6. //  time  is  0   $pastaPan  =  new  Pan();   $water  =  new  Water();   $pastaPan-­‐>fill($water);   $pastaPan-­‐>warm($duration  =  10);   //  now  time  is  10   $pastaPan-­‐>fill(new  Spaghetti());   $pastaPan-­‐>warm($duration  =  8);   //  now  time  is  18   $pastaPan-­‐>remove($water);   ! $saucePan  =  new  Pan();   $saucePan-­‐>fill(new  OliveOil());   $saucePan-­‐>warm($duration  =  2);   //  now  time  is  20   $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));   $saucePan-­‐>warm($duration  =  5);   //  now  time  is  25   $saucePan-­‐>fill(TomatoFactory::create());   $saucepan-­‐>warm($duration  =  4);   //  now  time  is  29   ! $plate  =  new  Plate();   $plate-­‐>addContentsOf($pastaPan);   $plate-­‐>addContentsOf($saucePan);   $plate-­‐>serve('Régalez-­‐vous');
  7. 7. LES 3 GRANDS TABOUS DU DEVELOPPEUR UTILISER eval()
  8. 8. LES 3 GRANDS TABOUS DU DEVELOPPEUR INJECTER UN CONTENEUR D’INJECTION DE DÉPENDANCE
  9. 9. LES 3 GRANDS TABOUS DU DEVELOPPEUR SERVIR DES PÂTES FROIDES
  10. 10. 2/10 NOTE DES INVITÉS
  11. 11. EVENTLOOP À LA RESCOUSSE
  12. 12. class  EventLoop   {      protected  $tick  =  0;      protected  $callbacksForTick  =  array();   !    public  function  executeLater($delay,  $callback)  {          $this-­‐>callbacksForTick[$this-­‐>tick  +  $delay]  []=  $callback;      }          public  function  start()  {          while  ($this-­‐>callbacksForTick)  {              $this-­‐>tick++;              $this-­‐>executeCallbacks();          }      }            public  function  executeCallbacks()  {          echo  "tic-­‐tac  :  "  .  $this-­‐>tick  .  "n";          if  (!isset($this-­‐>callbacksForTick[$this-­‐>tick]))  {              return;  //  no  callback  to  execute          }          foreach  ($this-­‐>callbacksForTick[$this-­‐>tick]  as  $callback)  {              call_user_func($callback);          }          //  clean  up          unset($this-­‐>callbacksForTick[$this-­‐>tick]);      }   }
  13. 13. LA CASSEROLE ASYNCHRONE
  14. 14. class  AsynchronousPan  extends  Pan   {      protected  $eventLoop;            public  function  __construct(EventLoop  $eventLoop)      {          $this-­‐>eventLoop  =  $eventLoop;      }            public  function  warm($duration,  $callback)      {          $this-­‐>eventLoop-­‐>executeLater($duration,  $callback);      }   }
  15. 15. $eventLoop  =  new  EventLoop();   ! $pan  =  new  AsynchronousPan($eventLoop);   $pan-­‐>warm(10,  function()  {      echo  "Régalez-­‐vousn";   });   ! echo  "Ca  chauffe  !n";   ! $eventLoop-­‐>start();
  16. 16. DÉMONSTRATION
  17. 17. Ca  chauffe  !   tic-­‐tac  :  1   tic-­‐tac  :  2   tic-­‐tac  :  3   tic-­‐tac  :  4   tic-­‐tac  :  5   tic-­‐tac  :  6   tic-­‐tac  :  7   tic-­‐tac  :  8   tic-­‐tac  :  9   tic-­‐tac  :  10   Régalez-­‐vous
  18. 18. LES SPAGHETTI ASYNCHRONES
  19. 19. $eventLoop  =  new  EventLoop();   ! $plate  =  new  Plate();   ! //  pasta   $pastaPan  =  new  AsynchronousPan($eventLoop);   $water  =  new  Water();   $pastaPan-­‐>fill($water);   echo  "pastaPan:  Allumagen";   $pastaPan-­‐>warm($duration  =  10,  function()  use  ($pastaPan,  $plate,  $water)  {      echo  "pastaPan:  L'eau  boutn";      $pastaPan-­‐>fill(new  Spaghetti());      echo  "pastaPan:  Lancement  de  la  cuisson  des  spaghettisn";      $pastaPan-­‐>warm($duration  =  8,  function()  use  ($pastaPan,  $plate,  $water){          echo  "pastaPan:  Les  spaghettis  sont  prêtsn";          $pastaPan-­‐>remove($water);          $plate-­‐>addContentsOf($pastaPan);      });   });   ../..
  20. 20. ../..   //  sauce   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      $saucePan  =  new  AsynchronousPan($eventLoop);      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  function()  use($saucePan,  $plate)  {          echo  "saucePan:  L'huile  est  chauden";          $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));          echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";          $saucePan-­‐>warm($duration  =  5,  function()  use($saucePan,  $plate)  {              echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";              $saucePan-­‐>fill(TomatoFactory::create());              echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";              $saucePan-­‐>warm($duration  =  4,  function()  use($saucePan,  $plate)  {                  echo  "saucePan:  La  sauce  est  prêten";                  $plate-­‐>addContentsOf($saucePan);              });          });      });   });   ! $eventLoop-­‐>start();   $plate-­‐>serve('Régalez-­‐vous');
  21. 21. Synchrone SÉQUENTIALITÉ Asynchrone
  22. 22. pastaPan:  Allumage   tic-­‐tac  :  1   ...   tic-­‐tac  :  7   saucePan:  L'huile  chauffe   tic-­‐tac  :  8   tic-­‐tac  :  9   saucePan:  L'huile  est  chaude   saucePan:  Lancement  de  la  cuisson  du  mirepoix   tic-­‐tac  :  10   pastaPan:  L'eau  bout   pastaPan:  Lancement  de  la  cuisson  des  spaghettis   tic-­‐tac  :  11   ....   tic-­‐tac  :  14   saucePan:  Le  mirepoix  est  prêt  pour  la  tomate   saucePan:  Lancement  de  la  cuisson  de  la  tomate   tic-­‐tac  :  15   ...   tic-­‐tac  :  18   pastaPan:  Les  spaghettis  sont  prêts   saucePan:  La  sauce  est  prête   Régalez-­‐vous
  23. 23. SCRIPT SYNCHRONE 29 MINUTES
  24. 24. SCRIPT ASYNCHRONE 18 MINUTES
  25. 25. 7/10 NOTE DES INVITÉS
  26. 26. class  EventLoop   {      protected  $tick  =  0;      protected  $callbacksForTick  =  array();   !    public  function  executeLater($delay,  $callback)  {          $this-­‐>callbacksForTick[$this-­‐>tick  +  $delay]  []=  $callback;      }          public  function  start()  {          while  ($this-­‐>callbacksForTick)  {              $this-­‐>tick++;              $this-­‐>executeCallbacks();          }      }            public  function  executeCallbacks()  {          echo  "tic-­‐tac  :  "  .  $this-­‐>tick  .  "n";          if  (!isset($this-­‐>callbacksForTick[$this-­‐>tick]))  {              return;  //  no  callback  to  execute          }          foreach  ($this-­‐>callbacksForTick[$this-­‐>tick]  as  $callback)  {              call_user_func($callback);          }          //  clean  up          unset($this-­‐>callbacksForTick[$this-­‐>tick]);      }   }
  27. 27. //  ...   $eventLoop-­‐>start();   ! //  Never  executed   $plate-­‐>serve('Régalez-­‐vous');
  28. 28. RESYNCHRONISER L’ASYNCHRONE
  29. 29. class  PlateOfSpaghettiWithSauce  extends  Plate   {      protected  $hasSpaghetti  =  false;      protected  $hasSauce  =  false;            public  function  addContentsOf(Pan  $pan)      {          parent::addContentsOf($pan);          if  ($pan-­‐>contains('Spaghetti'))  {              $this-­‐>hasSpaghetti  =  true;          }          if  ($pan-­‐>contains('Tomato'))  {              $this-­‐>hasSauce  =  true;          }          if  ($this-­‐>hasSpaghetti  &&  $this-­‐>hasSauce)  {              $this-­‐>serve('Régalez-­‐vous');          }      }   }
  30. 30. ../..   //  sauce   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      $saucePan  =  new  AsynchronousPan($eventLoop);      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  function()  use($saucePan,  $plate)  {          echo  "saucePan:  L'huile  est  chauden";          $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));          echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";          $saucePan-­‐>warm($duration  =  5,  function()  use($saucePan,  $plate)  {              echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";              $saucePan-­‐>fill(TomatoFactory::create());              echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";              $saucePan-­‐>warm($duration  =  4,  function()  use($saucePan,  $plate)  {                  echo  "saucePan:  La  sauce  est  prêten";                  $plate-­‐>addContentsOf($saucePan);              });          });      });   });   ! $eventLoop-­‐>start();   $plate-­‐>serve('Régalez-­‐vous');
  31. 31. DÉMÊLER LE CODE SPAGHETTI
  32. 32. $saucePan  =  new  AsynchronousPan($eventLoop);   $eventLoop-­‐>executeLater($delay  =  7,  function()  {      call_user_func($warmSaucePan);   });   $warmSaucePan  =  function()  use  ($saucePan)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $cookMirepoix);   };   $cookMirepoix  =  function()  use  ($saucePan)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $cookTomato);   };   $cookTomato  =  function()  use  ($saucePan)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $serveSauce);   };   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };
  33. 33. $saucePan  =  new  AsynchronousPan($eventLoop);   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };   $cookTomato  =  function()  use  ($saucePan,  $serveSauce)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $serveSauce);   };   $cookMirepoix  =  function()  use  ($saucePan,  $cookTomato)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $cookTomato);   };   $warmSaucePan  =  function()  use  ($saucePan,  $cookMirepoix)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $cookMirepoix);   };   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($warmSaucePan)  {          call_user_func($warmSaucePan);   });
  34. 34. $saucePan  =  new  AsynchronousPan($eventLoop);   $warmSaucePan  =  function($callback)  use  ($saucePan)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $callback);   };   $cookMirepoix  =  function($callback)  use  ($saucePan)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $callback);   };   $cookTomato  =  function($callback)  use  ($saucePan)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $callback);   };   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      Async::waterfall(          array($warmSaucePan,  $cookMirepoix,  $cookTomato),          $serveSauce      );   });
  35. 35. class  Async   {      public  static  function  waterfall($tasks,  $callback  =  null)      {              $taskCallback  =  function  ()  use  (&$next)  {                      call_user_func_array($next,  func_get_args());              };              $done  =  function  ()  use  ($callback)  {                      if  ($callback)  {                              call_user_func_array($callback,  func_get_args());                      }              };              $next  =  function  ()  use  (&$tasks,  $taskCallback,  $done)  {                      if  (0  ===  count($tasks))  {                              call_user_func_array($done,  func_get_args());                              return;                      }   !                    $task  =  array_shift($tasks);                      $args  =  array_merge(func_get_args(),  array($taskCallback));                      call_user_func_array($task,  $args);              };              $next();      }   } Source: https://github.com/reactphp/async/blob/master/src/React/Async/Util.php#L81
  36. 36. EN ASYNCHRONE PAS DE RETURN
  37. 37. //  prototype  synchronous  function   $cook  =  function($ingredient)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5);      return  'chaud  devant';   };   ! ! $message  =  $cook(MirepoixFactory::create());   echo  $message,  "n";  
  38. 38. //  prototype  asynchronous  function   $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $callback('chaud  devant');      });   };   ! $cook(MirepoixFactory::create(),  function($message)  {      echo  $message,  "n";   });
  39. 39. EN ASYNCHRONE PAS DE TRY/CATCH
  40. 40. $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $isSummer  =  in_array(date('m'),    array(6,  7,  8)):          if  ($saucePan-­‐>contains('Tomato')  &&  !$isSummer)  {              throw  new  OutOfBoundsException('On  ne  fait                    de  la  bonne  sauce  tomate  qu'en  été');          }          $callback('chaud  devant  !');      });   };   ! try  {      $cook(MirepoixFactory::create(),  function($message)  {          echo  $message,  "n";      });     }  catch  (OutOfBoundsException  $e)  {      echo  "Echec  de  la  recetten";   }
  41. 41. $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $isSummer  =  in_array(date('m'),    array(6,  7,  8)):          if  ($saucePan-­‐>contains('Tomato')  &&  !$isSummer)  {              return  callback(new  OutOfBoundsException('On  ne  fait                    de  la  bonne  sauce  tomate  qu'en  ete'));          }          $callback(null,  'chaud  devant  !');      });   };   ! $cook(MirepoixFactory::create(),  function($error,  $message)  {      if  ($error  instanceOf  OutOfBoundsException)  {          echo  "Echec  de  la  recetten";          return;      }      echo  $message,  "n";   });
  42. 42. DU CODE RÉUTILISABLE ROBUSTE UN PLAT CHAUD
  43. 43. VICTOIRE !
  44. 44. 10/10 NOTE DES INVITÉS
  45. 45. IL N’Y A TOUJOURS QU’UN SEUL CHEF
  46. 46. PROGRAMMATION NON PARALLELE
  47. 47. PAS DE TEMPS PERDU ARÉPONSE D’UN AUTRE ATTENDRE LA
  48. 48. LATENCE I/O Operation                          CPU  cycles   L1  ..................  3   L2  ..................  14   RAM  .................  250   Disk  ................  41,000,000   Network  .............  240,000,000
  49. 49. L1  cache  reference  .........................  0.5  ns   L2  cache  reference  ...........................  7  ns   Main  memory  reference  ......................  100  ns                           Compress  1K  bytes  with  Zippy  .............  3,000  ns    =      3  µs   Send  2K  bytes  over  1  Gbps  network  .......  20,000  ns    =    20  µs   SSD  random  read  ........................  150,000  ns    =  150  µs   Read  1  MB  sequentially  from  memory  .....  250,000  ns    =  250  µs   Round  trip  within  same  datacenter  ......  500,000  ns    =  0.5  ms   Read  1  MB  sequentially  from  SSD*  .....  1,000,000  ns    =      1  ms   Disk  seek  ...........................  10,000,000  ns    =    10  ms   Read  1  MB  sequentially  from  disk  ....  20,000,000  ns    =    20  ms   Send  packet  CA-­‐>Netherlands-­‐>CA  ....  150,000,000  ns    =  150  ms Source: http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf
  50. 50. I/O = ATTENTE ETABLISSEMENT CONNEXION SÉCURISÉE RÉCEPTION REQUÊTE HTTP REQUÊTE BASE DE DONNÉES LECTURE VALEUR DANS MEMCACHE LECTURE DE FICHIER SUR DISQUE APPEL À UNE API REST ENVOI MESSAGE À UN AMQP ENVOI RÉPONSE HTTP
  51. 51. 90% DU TEMPS DE RÉPONSE D’UNE REQUÊTE HTTP EST PASSÉ À ATTENDRE UNE I/O
  52. 52. GET  /favicon.ico Process serveur Routing Lancement de l’ordre de chargement du fichier Déplacement de la tête de lecture Transfert des données du disque en mémoire Construction de la réponse HTTP Traitement Attente Envoi de la réponse HTTP Fin de la requête
  53. 53. UN SERVEUR WEB PASSE SON TEMPS A SE TOURNER LES POUCES
  54. 54. POUR MIEUX UTILISER LE CPU ON MULTIPLIE LES PROCESS LESLA CONSO MÉMOIRE THREADS ET DONC
  55. 55. MaxClients  50
  56. 56. UN AUTRE MONDE EST POSSIBLE
  57. 57. GET  /favicon.ico Process serveur Transfert des données du disque en mémoire I/O disque asynchrone Envoi de la réponse HTTP I/O réseau asynchrone Traitement Attente
  58. 58. LIBEL EVENT LOOP LIBUV LIBEIO ASYNCHRONOUS I/O MULTI-PLATFORM ABSTRACTION LAYER
  59. 59. GET  /favicon.ico GET  /js/jquery.js GET  /css/main.css
  60. 60. COMMENT FAIRE DES I/O ASYNCHRONES EN PHP ?
  61. 61. Event-­‐driven,  non-­‐blocking  I/O  with  PHP.  
  62. 62. PHP & PROCESS PERSISTENTS PAS BON MÉNAGE
  63. 63. PHP N’A PAS DE FONCTIONS D’I/O DISQUE ASYNCHRONES PECL/LIBIO
  64. 64. TANT QU’À INSTALLER UN BINAIRE AUTANT EN PRENDRE UN STABLE POPULAIRE
  65. 65. TANT QU’À REPOSER SUR UNE EVENT LOOP AUTANT UTILISER UN LANGAGE ÉVÈNEMENTIEL
  66. 66. TANT QU’À ABUSER DES FONCTIONS ANONYMES AUTANT UTILISER UN LANGAGE FONCTIONNEL
  67. 67. var  fs  =  require('fs');   fs.unlink('/tmp/hello',  function  (err)  {      if  (err)  throw  err;      console.log('successfully  deleted  /tmp/hello');   });   //  more  code   console.log('deletion  script');
  68. 68. plate  =  new  Plate();   pastaPan  =  new  AsynchronousPan(eventLoop);   water  =  new  Water();   pastaPan.fill(water);   console.log('pastaPan:  Starting  to  boil  water');   pastaPan.warm(duration  =  10,  function()  {      console.log('pastaPan:  Water  is  boiling');      pastaPan.fill(new  Spaghetti());      console.log('pastaPan:  Starting  to  boil  spaghetti');      pastaPan.warm(duration  =  8,  function()  {          console.log('pastaPan:  Spaghetti  is  ready');          pastaPan.remove(water);          plate.addContentsOf(pastaPan);      });   });
  69. 69. $plate  =  new  Plate();   $pastaPan  =  new  AsynchronousPan($eventLoop);   $water  =  new  Water();   $pastaPan-­‐>fill($water);   echo  "pastaPan:  Starting  to  boil  watern";   $pastaPan-­‐>warm($duration  =  10,  function()  use  ($pastaPan,  $plate,  $water)  {      echo  "pastaPan:  Water  is  boilingn";      $pastaPan-­‐>fill(new  Spaghetti());      echo  "pastaPan:  Starting  to  boil  spaghettin";      $pastaPan-­‐>warm($duration  =  8,  function()  use  ($pastaPan,  $plate,  $water)  {          echo  "pastaPan:  Spaghetti  is  readyn";          $pastaPan-­‐>remove($water);          $plate-­‐>addContentsOf($pastaPan);      });   });
  70. 70. I/O ASYNCHRONES UN SEUL CHEF DES CALLBACKS PLUS DE CONCURRENCE MOINS DE CONSOL MÉMOIRE PAS POSSIBLE EN PHP, NATIF EN NODE.JS PAS DE RETURN, PAS DE TRY/CATCH
  71. 71. DES PÂTES CHAUDES
  72. 72. MERCI François Zaninotto - @francoisz - marmelab.com github.com/fzaninotto - github.com/marmelab

×