抽象异步模型

对回调模型抽象出异步接口Async

只有一个方法的接口通常都可以使用闭包代替,区别在于interface引入新类型,闭包则不会。如果说thunkify依赖了参数顺序的弱约定,Async相对严肃的依赖了类型。

  1. <?php
  2. interface Async
  3. {
  4. public function begin(callable $callback);
  5. }
  6. // AsyncTask符合Async定义, 实现Async
  7. final class AsyncTask implements Async
  8. {
  9. public function next($result = null)
  10. {
  11. $value = $this->gen->send($result);
  12. if ($this->gen->valid()) {
  13. // \Generator -> Async
  14. if ($value instanceof \Generator) {
  15. $value = new self($value);
  16. }
  17. if ($value instanceof Async) {
  18. $async = $value;
  19. $continuation = [$this, "next"];
  20. $async->begin($continuation);
  21. } else {
  22. $this->next($value);
  23. }
  24. } else {
  25. $cc = $this->continuation;
  26. $cc($result);
  27. }
  28. }
  29. }

两个简单的对回调接口转换例子:

  1. <?php
  2. // 定时器修改为标准异步接口
  3. class AsyncSleep implements Async
  4. {
  5. public function begin(callable $cc)
  6. {
  7. swoole_timer_after(1000, $cc);
  8. }
  9. }
  10. // 异步dns查询修改为标准异步接口
  11. class AsyncDns implements Async
  12. {
  13. public function begin(callable $cc)
  14. {
  15. swoole_async_dns_lookup("www.baidu.com", function($host, $ip) use($cc) {
  16. // 这里我们会发现, 通过call $cc, 将返回值作为参数进行传递, 与callcc相像
  17. // $ip 通过$cc 从子生成器传入父生成器, 最终通过send方法成为yield表达式结果
  18. $cc($ip);
  19. });
  20. }
  21. }
  22. function newGen()
  23. {
  24. $r1 = (yield newSubGen());
  25. $r2 = (yield 2);
  26. $start = time();
  27. yield new AsyncSleep();
  28. echo time() - $start, "\n";
  29. $ip = (yield new AsyncDns());
  30. yield "IP: $ip";
  31. }
  32. $task = new AsyncTask(newGen());
  33. $trace = function($r) { echo $r; };
  34. $task->begin($trace);
  35. // output:
  36. // 1
  37. // IP: 115.239.210.27