引入异常处理

尽管只有寥寥几行代码,我们却已经实现了可工作的半协程调度器(缺失异常处理)。

没关系,下面先rollback回return的实现,开始引入异常处理,目标是在嵌套生成器之间正确向上抛出异常,跨生成器捕获异常。

  1. <?php
  2. // 为Gen引入throw方法
  3. class Gen
  4. {
  5. // PHP7 之前 关键词不能用作名字
  6. public function throw_(\Exception $ex)
  7. {
  8. return $this->generator->throw($ex);
  9. }
  10. }
  11. final class AsyncTask
  12. {
  13. public function begin()
  14. {
  15. return $this->next();
  16. }
  17. // 这里添加第二个参数, 用来在迭代过程传递异常
  18. public function next($result = null, \Exception $ex = null)
  19. {
  20. if ($ex) {
  21. $this->gen->throw_($ex);
  22. }
  23. $ex = null;
  24. try {
  25. // send方法内部是一个resume的过程:
  26. // 恢复execute_data上下文, 调用zend_execute_ex()继续执行,
  27. // 后续中op_array内可能会抛出异常
  28. $value = $this->gen->send($result);
  29. } catch (\Exception $ex) {}
  30. if ($ex) {
  31. if ($this->gen->valid()) {
  32. // 传递异常
  33. return $this->next(null, $ex);
  34. } else {
  35. throw $ex;
  36. }
  37. } else {
  38. if ($this->gen->valid()) {
  39. // 正常yield值
  40. return $this->next($value);
  41. } else {
  42. return $result;
  43. }
  44. }
  45. }
  46. }
  1. <?php
  2. function newGen()
  3. {
  4. $r1 = (yield 1);
  5. throw new \Exception("e");
  6. $r2 = (yield 2);
  7. yield 3;
  8. }
  9. $task = new AsyncTask(newGen());
  10. try {
  11. $r = $task->begin();
  12. echo $r;
  13. } catch (\Exception $ex) {
  14. echo $ex->getMessage(); // output: e
  15. }