SoFunction
Updated on 2025-03-10

Analysis of asynchronous method based on coroutines

This article describes the method of php implementing asynchronous based on coroutines. Share it for your reference, as follows:

Most of the coroutines in php on github are implemented based on this article:/2012/12/22/

Their final result is to turn the callback into code that executes gracefully in sequence, but it is still blocking, not really asynchronous.

For example, the most popular:/recoilphp/recoil

Install first:

composer require recoil/recoil

implement:

<?php
//
include __DIR__ . '/vendor/';
use Recoil\React\ReactKernel;
$i = 100000;
ReactKernel::start(task1());
ReactKernel::start(task2());
function task1(){
  global $i;
  echo "wait start" . PHP_EOL;
  while ($i-- > 0) {
    yield;
  }
  echo "wait end" . PHP_EOL;
};
function task2(){
  echo "Hello " . PHP_EOL;
  yield;
  echo "world!" . PHP_EOL;
}

result:

wait start
//Wait for several seconds
wait end
Hello
world!

I originally wanted to make the two tasks parallel, but the two tasks became serial, and I couldn't do anything during the waiting time. React responsive programming strictly prohibits this kind of waiting, so I wrote a php version by referring to the unity3d coroutine. On code:

&lt;?php
//
//The timer relies on swoole implementation, and other methods can also be used to implement the timerclass Coroutine
{
  //The timer interval can be changed as needed, unit ms  const TICK_INTERVAL = 1;
  private $routineList;
  private $tickId = -1;
  public function __construct()
  {
    $this-&gt;routineList = [];
  }
  public function start(Generator $routine)
  {
    $task = new Task($routine);
    $this-&gt;routineList[] = $task;
    $this-&gt;startTick();
  }
  public function stop(Generator $routine)
  {
    foreach ($this-&gt;routineList as $k =&gt; $task) {
      if($task-&gt;getRoutine() == $routine){
        unset($this-&gt;routineList[$k]);
      }
    }
  }
  private function startTick()
  {
    swoole_timer_tick(self::TICK_INTERVAL, function($timerId){
      $this-&gt;tickId = $timerId;
      $this-&gt;run();
    });
  }
  private function stopTick()
  {
    if($this-&gt;tickId &gt;= 0) {
      swoole_timer_clear($this-&gt;tickId);
    }
  }
  private function run()
  {
    if(empty($this-&gt;routineList)){
      $this-&gt;stopTick();
      return;
    }
    foreach ($this-&gt;routineList as $k =&gt; $task) {
      $task-&gt;run();
      if($task-&gt;isFinished()){
        unset($this-&gt;routineList[$k]);
      }
    }
  }
  
}
class Task
{
  protected $stack;
  protected $routine;
  public function __construct(Generator $routine)
  {
    $this-&gt;routine = $routine;
    $this-&gt;stack = new SplStack();
  }
  /**
    * [run coroutine schedule]
    * @return [type] [description]
    */
  public function run()
  {
    $routine = &amp;$this-&gt;routine;
    try {
      if(!$routine){
        return;
      }
      $value = $routine-&gt;current();
      //Nested coroutine      if ($value instanceof Generator) {
        $this-&gt;stack-&gt;push($routine);
        $routine = $value;
        return;
      }
      //Nested coroutine returns      if(!$routine-&gt;valid() &amp;&amp; !$this-&gt;stack-&gt;isEmpty()) {
        $routine = $this-&gt;stack-&gt;pop();
      }
      $routine-&gt;next();
    } catch (Exception $e) {
      if ($this-&gt;stack-&gt;isEmpty()) {
        /*
          throw the exception
        */
        return;
      }
    }
  }
  /**
    * [isFinished determines whether the task is completed]
    * @return boolean [description]
    */
  public function isFinished()
  {
    return $this-&gt;stack-&gt;isEmpty() &amp;&amp; !$this-&gt;routine-&gt;valid();
  }
  public function getRoutine()
  {
    return $this-&gt;routine;
  }
}

Test code:

<?php
//
 require '';
$i = 10000;
$c = new Coroutine();
$c->start(task1());
$c->start(task2());
function task1(){
  global $i;
  echo "wait start" . PHP_EOL;
  while ($i-- > 0) {
    yield;
  }
  echo "wait end" . PHP_EOL;
};
function task2(){
  echo "Hello " . PHP_EOL;
  yield;
  echo "world!" . PHP_EOL;
}

result:

wait start
Hello
world!
//Wait for a few seconds, but don't block
wait end

For more information about PHP related content, please check out the topic of this site:PHP extension development tutorial》、《Summary of PHP network programming skills》、《Summary of the usage of php curl》、《Complete collection of PHP array (Array) operation techniques》、《PHP data structure and algorithm tutorial》、《Summary of PHP Programming Algorithm"and"Summary of usage of php strings

I hope this article will be helpful to everyone's PHP programming.