异常: 重新进行CPS变换

我们把加入异常处理的代码重新修改为CPS方式:

  1. <?php
  2. final class AsyncTask
  3. {
  4. public $continuation;
  5. public function begin(callable $continuation)
  6. {
  7. $this->continuation = $continuation;
  8. $this->next();
  9. }
  10. public function next($result = null, \Exception $ex = null)
  11. {
  12. try {
  13. if ($ex) {
  14. $value = $this->gen->throw_($ex);
  15. } else {
  16. $value = $this->gen->send($result);
  17. }
  18. if ($this->gen->valid()) {
  19. if ($value instanceof \Generator) {
  20. // 注意这里
  21. $continuation = [$this, "next"];
  22. (new self($value))->begin($continuation);
  23. } else {
  24. $this->next($value);
  25. }
  26. } else {
  27. // 迭代结束 返回结果
  28. $cc = $this->continuation; // cc指向 父生成器next方法 或 用户传入continuation
  29. $cc($result, null);
  30. }
  31. } catch (\Exception $ex) {
  32. if ($this->gen->valid()) {
  33. // 抛出异常
  34. $this->next(null, $ex);
  35. } else {
  36. // 未捕获异常
  37. $cc = $this->continuation; // cc指向 父生成器next方法 或 用户传入continuation
  38. $cc(null, $ex);
  39. }
  40. }
  41. }
  42. }
  1. <?php
  2. function tt()
  3. {
  4. yield;
  5. throw new \Exception("e");
  6. }
  7. function t()
  8. {
  9. yield tt();
  10. yield 1;
  11. }
  12. $task = new AsyncTask(t());
  13. $trace = function($r, $ex) {
  14. if ($ex) {
  15. echo $ex->getMessage(); // output: e
  16. } else {
  17. echo $r;
  18. }
  19. };
  20. $task->begin($trace);
  1. <?php
  2. function newSubGen()
  3. {
  4. yield 0;
  5. throw new \Exception("e");
  6. yield 1;
  7. }
  8. function newGen()
  9. {
  10. try {
  11. $r1 = (yield newSubGen());
  12. } catch (\Exception $ex) {
  13. echo $ex->getMessage(); // output: e
  14. }
  15. $r2 = (yield 2);
  16. yield 3;
  17. }
  18. $task = new AsyncTask(newGen());
  19. $trace = function($r, $ex) {
  20. if ($ex) {
  21. echo $ex->getMessage();
  22. } else {
  23. echo $r; // output: 3
  24. }
  25. };
  26. $task->begin($trace); // output: e