La programmation asynchrone... et les pates
Upcoming SlideShare
Loading in...5
×
 

La programmation asynchrone... et les pates

on

  • 1,025 views

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

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

Statistics

Views

Total Views
1,025
Views on SlideShare
1,024
Embed Views
1

Actions

Likes
1
Downloads
4
Comments
0

1 Embed 1

https://twitter.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs LicenseCC Attribution-NonCommercial-NoDerivs License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    La programmation asynchrone... et les pates La programmation asynchrone... et les pates Presentation Transcript

    • LA PROGRAMMATION ASYNCHRONE ET LES PÂTES François Zaninotto - @francoisz - marmelab.com
    • PARLONS CUISINE
    • 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
    • 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
    • MAINTENANT LE CHEF C’EST PHP
    • //  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');
    • LES 3 GRANDS TABOUS DU DEVELOPPEUR UTILISER eval()
    • LES 3 GRANDS TABOUS DU DEVELOPPEUR INJECTER UN CONTENEUR D’INJECTION DE DÉPENDANCE
    • LES 3 GRANDS TABOUS DU DEVELOPPEUR SERVIR DES PÂTES FROIDES
    • 2/10 NOTE DES INVITÉS
    • EVENTLOOP À LA RESCOUSSE
    • 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]);      }   }
    • LA CASSEROLE ASYNCHRONE
    • class  AsynchronousPan  extends  Pan   {      protected  $eventLoop;            public  function  __construct(EventLoop  $eventLoop)      {          $this-­‐>eventLoop  =  $eventLoop;      }            public  function  warm($duration,  $callback)      {          $this-­‐>eventLoop-­‐>executeLater($duration,  $callback);      }   }
    • $eventLoop  =  new  EventLoop();   ! $pan  =  new  AsynchronousPan($eventLoop);   $pan-­‐>warm(10,  function()  {      echo  "Régalez-­‐vousn";   });   ! echo  "Ca  chauffe  !n";   ! $eventLoop-­‐>start();
    • DÉMONSTRATION
    • 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
    • LES SPAGHETTI ASYNCHRONES
    • $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);      });   });   ../..
    • ../..   //  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');
    • Synchrone SÉQUENTIALITÉ Asynchrone
    • 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
    • SCRIPT SYNCHRONE 29 MINUTES
    • SCRIPT ASYNCHRONE 18 MINUTES
    • 7/10 NOTE DES INVITÉS
    • 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]);      }   }
    • //  ...   $eventLoop-­‐>start();   ! //  Never  executed   $plate-­‐>serve('Régalez-­‐vous');
    • RESYNCHRONISER L’ASYNCHRONE
    • 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');          }      }   }
    • ../..   //  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');
    • DÉMÊLER LE CODE SPAGHETTI
    • $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);   };
    • $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);   });
    • $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      );   });
    • 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
    • EN ASYNCHRONE PAS DE RETURN
    • //  prototype  synchronous  function   $cook  =  function($ingredient)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5);      return  'chaud  devant';   };   ! ! $message  =  $cook(MirepoixFactory::create());   echo  $message,  "n";  
    • //  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";   });
    • EN ASYNCHRONE PAS DE TRY/CATCH
    • $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";   }
    • $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";   });
    • DU CODE RÉUTILISABLE ROBUSTE UN PLAT CHAUD
    • VICTOIRE !
    • 10/10 NOTE DES INVITÉS
    • IL N’Y A TOUJOURS QU’UN SEUL CHEF
    • PROGRAMMATION NON PARALLELE
    • PAS DE TEMPS PERDU ARÉPONSE D’UN AUTRE ATTENDRE LA
    • LATENCE I/O Operation                          CPU  cycles   L1  ..................  3   L2  ..................  14   RAM  .................  250   Disk  ................  41,000,000   Network  .............  240,000,000
    • 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
    • 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
    • 90% DU TEMPS DE RÉPONSE D’UNE REQUÊTE HTTP EST PASSÉ À ATTENDRE UNE I/O
    • 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
    • UN SERVEUR WEB PASSE SON TEMPS A SE TOURNER LES POUCES
    • POUR MIEUX UTILISER LE CPU ON MULTIPLIE LES PROCESS LESLA CONSO MÉMOIRE THREADS ET DONC
    • MaxClients  50
    • UN AUTRE MONDE EST POSSIBLE
    • 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
    • LIBEL EVENT LOOP LIBUV LIBEIO ASYNCHRONOUS I/O MULTI-PLATFORM ABSTRACTION LAYER
    • GET  /favicon.ico GET  /js/jquery.js GET  /css/main.css
    • COMMENT FAIRE DES I/O ASYNCHRONES EN PHP ?
    • Event-­‐driven,  non-­‐blocking  I/O  with  PHP.  
    • PHP & PROCESS PERSISTENTS PAS BON MÉNAGE
    • PHP N’A PAS DE FONCTIONS D’I/O DISQUE ASYNCHRONES PECL/LIBIO
    • TANT QU’À INSTALLER UN BINAIRE AUTANT EN PRENDRE UN STABLE POPULAIRE
    • TANT QU’À REPOSER SUR UNE EVENT LOOP AUTANT UTILISER UN LANGAGE ÉVÈNEMENTIEL
    • TANT QU’À ABUSER DES FONCTIONS ANONYMES AUTANT UTILISER UN LANGAGE FONCTIONNEL
    • 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');
    • 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);      });   });
    • $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);      });   });
    • 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
    • DES PÂTES CHAUDES
    • MERCI François Zaninotto - @francoisz - marmelab.com github.com/fzaninotto - github.com/marmelab