zoukankan      html  css  js  c++  java
  • PHP下的异步尝试四:PHP版的Promise

    PHP下的异步尝试系列

    如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅

    1. PHP下的异步尝试一:初识生成器
    2. PHP下的异步尝试二:初识协程
    3. PHP下的异步尝试三:协程的PHP版thunkify自动执行器
    4. PHP下的异步尝试四:PHP版的Promise
    5. PHP下的异步尝试五:PHP版的Promise的继续完善

    Promise 实现

    代码结构

    
    │  │  autoload.php
    │  │  promise1.php
    │  │  promise2.php
    │  │  promise3.php
    │  │  promise4.php
    │  │  promise5.php
    │  │
    │  └─classes
    │          Promise1.php
    │          Promise2.php
    │          Promise3.php
    │          Promise4.php
    │          Promise5.php
    │          PromiseState.php
    

    尝试一 (Promise基础)

    classess/PromiseState.php

    
    final class PromiseState
    {
        const PENDING = 'pending';
        const FULFILLED = 'fulfilled';
        const REJECTED = 'rejected';
    }
    

    classess/Promise1.php

    
    // 尝试一
    class Promise1
    {
        private $value;
        private $reason;
        private $state;
    
        public function __construct(Closure $func = null)
        {
            $this->state = PromiseState::PENDING;
    
            $func([$this, 'resolve'], [$this, 'reject']);
        }
    
        /**
         * 执行回调方法里的resolve绑定的方法
         * @param null $value
         */
        public function resolve($value = null)
        {
            // 回调执行resolve传参的值,赋值给result
            $this->value = $value;
    
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::FULFILLED;
            }
        }
    
        public function reject($reason = null)
        {
            // 回调执行resolve传参的值,赋值给result
            $this->reason = $reason;
    
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::REJECTED;
            }
        }
    
        public function getState()
        {
            return $this->state;
        }
    
        public function getValue()
        {
            return $this->value;
        }
    
        public function getReason()
        {
            return $this->reason;
        }
    }
    

    promise1.php

    
    require "autoload.php";
    
    $promise = new Promise1(function($resolve, $reject) {
        $resolve("打印我");
    });
    
    var_dump($promise->getState());
    var_dump($promise->getValue());
    

    结果:

    
    string(9) "fulfilled"
    string(9) "打印我"
    

    结论或问题:

    
    我们在这里建构了最基础的Promise模型
    

    尝试二 (增加链式then)

    classess/Promise2.php

    
    <?php
    // 尝试二 (增加链式then)
    class Promise2
    {
        private $value;
        private $reason;
        private $state;
    
        public function __construct(Closure $func = null)
        {
            $this->state = PromiseState::PENDING;
    
            $func([$this, 'resolve'], [$this, 'reject']);
        }
    
        public function then(Closure $onFulfilled = null, Closure $onRejected = null)
        {
            // 如果状态是fulfilled,直接回调执行并传参value
            if ($this->state == PromiseState::FULFILLED) {
                $onFulfilled($this->value);
            }
    
            // 如果状态是rejected,直接回调执行并传参reason
            if ($this->state == PromiseState::REJECTED) {
                $onRejected($this->reason);
            }
    
            // 返回对象自身,实现链式调用
            return $this;
    
        }
    
        /**
         * 执行回调方法里的resolve绑定的方法
         * 本状态只能从pending->fulfilled
         * @param null $value
         */
        public function resolve($value = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::FULFILLED;
                $this->value = $value;
            }
        }
    
        /**
         * 执行回调方法里的rejected绑定的方法
         * 本状态只能从pending->rejected
         * @param null $reason
         */
        public function reject($reason = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::REJECTED;
                $this->reason = $reason;
            }
        }
    
        public function getState()
        {
            return $this->state;
        }
    
        public function getValue()
        {
            return $this->value;
        }
    
        public function getReason()
        {
            return $this->reason;
        }
    }
    

    promise2.php

    
    <?php
    
    require "autoload.php";
    
    $promise = new Promise2(function($resolve, $reject) {
        $resolve("打印我");
    });
    
    $promise->then(function ($value) {
        var_dump($value);
    }, function ($reason) {
        var_dump($reason);
    })->then(function ($value) {
        var_dump($value);
    }, function ($reason) {
        var_dump($reason);
    });
    

    结果:

    
    string(9) "打印我"
    string(9) "打印我"
    

    结论或问题:

    
    我们实现了链式then方法
    
    如果我们的构造里的回调是异步执行的话,那么状态在没有变成fulfilled之前,我们then里的回调方法就永远没法执行
    

    尝试三(真正的链式then)

    classess/Promise3.php

    
    // 解决思路:我们肯定要把then传入的回调,放到Promise构造里回调代码执行完后resolve调用后改变了state状态后再调用,所以我们必须存储到一个地方并方便后续调用
    // 我们需要改造then、resolve和reject方法
    class Promise3
    {
        private $value;
        private $reason;
        private $state;
        private $fulfilledCallbacks = [];
        private $rejectedCallbacks = [];
    
        public function __construct(Closure $func = null)
        {
            $this->state = PromiseState::PENDING;
    
            $func([$this, 'resolve'], [$this, 'reject']);
        }
    
        public function then(Closure $onFulfilled = null, Closure $onRejected = null)
        {
            // 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
            if ($this->state == PromiseState::PENDING) {
                $this->fulfilledCallbacks[] = static function() use ($onFulfilled, $value){
                    $onFulfilled($this->value);
                };
    
                $this->rejectedCallbacks[] = static function() use ($onRejected, $reason){
                    $onRejected($this->reason);
                };
            }
    
            // 如果状态是fulfilled,直接回调执行并传参value
            if ($this->state == PromiseState::FULFILLED) {
                $onFulfilled($this->value);
            }
    
            // 如果状态是rejected,直接回调执行并传参reason
            if ($this->state == PromiseState::REJECTED) {
                $onRejected($this->reason);
            }
    
            // 返回对象自身,实现链式调用
            return $this;
    
        }
    
        /**
         * 执行回调方法里的resolve绑定的方法
         * 本状态只能从pending->fulfilled
         * @param null $value
         */
        public function resolve($value = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::FULFILLED;
                $this->value = $value;
    
                array_walk($this->fulfilledCallbacks, function ($callback) {
                    $callback();
                });
            }
        }
    
        /**
         * 执行回调方法里的rejected绑定的方法
         * 本状态只能从pending->rejected
         * @param null $reason
         */
        public function reject($reason = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::REJECTED;
                $this->reason = $reason;
            }
        }
    
        public function getState()
        {
            return $this->state;
        }
    
        public function getValue()
        {
            return $this->value;
        }
    
        public function getReason()
        {
            return $this->reason;
        }
    }
    

    promise3.php

    
    require "autoload.php";
    
    $promise = new Promise3(function($resolve, $reject) {
        $resolve("打印我");
    });
    
    $promise->then(function ($value) {
        var_dump($value);
    }, function ($reason) {
        var_dump($reason);
    })->then(function ($value) {
        var_dump($value);
    }, function ($reason) {
        var_dump($reason);
    });
    

    结果:

    
    string(9) "打印我"
    string(9) "打印我"
    

    结论或问题:

    
    我们这次基本实现了真正的链式then方法
    
    不过在Promise/A+里规范,要求then返回每次都要求是一个新的Promise对象
    then方法成功执行,相当于返回一个实例一个Promise回调里执行resolve方法,resolve值为then里return的值
    then方法执行失败或出错,相当于返回一个实例一个Promise回调里执行rejected方法,rejected值为then里return的值
    

    尝试四(then返回pormise对象, 并传递上一次的结果给下一个Promise对象)

    classess/Promise4.php

    
    class Promise4
    {
        private $value;
        private $reason;
        private $state;
        private $fulfilledCallbacks = [];
        private $rejectedCallbacks = [];
    
        public function __construct(Closure $func = null)
        {
            $this->state = PromiseState::PENDING;
    
            $func([$this, 'resolve'], [$this, 'reject']);
        }
    
        public function then(Closure $onFulfilled = null, Closure $onRejected = null)
        {
            $thenPromise = new Promise4(function ($reslove, $reject) use (&$thenPromise, $onFulfilled, $onRejected) {
    
                //$this 代表的当前的Promise对象,不要混淆了
    
                // 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
                if ($this->state == PromiseState::PENDING) {
                    $this->fulfilledCallbacks[] = static function() use ($thenPromise, $onFulfilled, $reslove, $reject){
                        $value = $onFulfilled($this->value);
                        $this->resolvePromise($thenPromise, $value, $reslove, $reject);
                    };
    
                    $this->rejectedCallbacks[] = static function() use ($thenPromise, $onRejected, $reslove, $reject){
                        $reason = $onRejected($this->reason);
                        $this->resolvePromise($thenPromise, $reason, $reslove, $reject);
                    };
                }
    
                // 如果状态是fulfilled,直接回调执行并传参value
                if ($this->state == PromiseState::FULFILLED) {
                    $value = $onFulfilled($this->value);
                    $this->resolvePromise($thenPromise, $value, $reslove, $reject);
                }
    
                // 如果状态是rejected,直接回调执行并传参reason
                if ($this->state == PromiseState::REJECTED) {
                    $reason = $onRejected($this->reason);
                    $this->resolvePromise($thenPromise, $reason, $reslove, $reject);
                }
    
            });
    
            // 返回对象自身,实现链式调用
            return $thenPromise;
    
        }
    
        /**
         * 解决Pormise链式then传递
         * 可参考 [Promises/A+]2.3 [https://promisesaplus.com/#the-promise-resolution-procedure]
         * @param $thenPromise
         * @param $x            $x为thenable对象
         * @param $resolve
         * @param $reject
         */
        private function resolvePromise($thenPromise, $x, $resolve, $reject)
        {
            $called = false;
    
            if ($thenPromise === $x) {
                return $reject(new Exception('循环引用'));
            }
    
            if ( is_object($x) && method_exists($x, 'then')) {
    
                $resolveCb = function ($value) use($thenPromise, $resolve, $reject, $called) {
                    if ($called) return ;
                    $called = true;
                    // 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
                    $this->resolvePromise($thenPromise, $value, $resolve, $reject);
                };
    
                $rejectCb = function($reason) use($thenPromise, $resolve, $reject, $called) {
                    if ($called) return ;
                    $called = true;
                    $reject($reason);
                };
    
                call_user_func_array([$x, 'then'], [$resolveCb, $rejectCb]);
    
            } else {
                if ($called) return ;
                $called = true;
                $resolve($x);
            }
        }
    
        /**
         * 执行回调方法里的resolve绑定的方法
         * 本状态只能从pending->fulfilled
         * @param null $value
         */
        public function resolve($value = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::FULFILLED;
                $this->value = $value;
    
                array_walk($this->fulfilledCallbacks, function ($callback) {
                    $callback();
                });
            }
        }
    
        /**
         * 执行回调方法里的rejected绑定的方法
         * 本状态只能从pending->rejected
         * @param null $reason
         */
        public function reject($reason = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::REJECTED;
                $this->reason = $reason;
            }
        }
    
        public function getState()
        {
            return $this->state;
        }
    
        public function getValue()
        {
            return $this->value;
        }
    
        public function getReason()
        {
            return $this->reason;
        }
    }
    

    promise4.php

    
    require "autoload.php";
    
    $promise1 = new Promise4(function($resolve, $reject) {
        $resolve("打印我");
    });
    
    $promise2 = $promise1->then(function ($value) {
        var_dump($value);
        return "promise2";
    }, function ($reason) {
        var_dump($reason);
    });
    
    $promise3 = $promise2->then(function ($value) {
        var_dump($value);
        return new Promise4(function($resolve, $reject) {
            $resolve("promise3");
        });
    }, function ($reason) {
        var_dump($reason);
    });
    
    $promise4 = $promise3->then(function ($value) {
        var_dump($value);
        return "promise4";
    }, function ($reason) {
        var_dump($reason);
    });
    
    var_dump($promise4);
    

    结果:

    
    string(9) "打印我"
    string(8) "promise2"
    string(8) "promise3"
    object(Promise4)#15 (5) {
      ["value":"Promise4":private]=>
      string(8) "promise4"
      ["reason":"Promise4":private]=>
      NULL
      ["state":"Promise4":private]=>
      string(9) "fulfilled"
      ["fulfilledCallbacks":"Promise4":private]=>
      array(0) {
      }
      ["rejectedCallbacks":"Promise4":private]=>
      array(0) {
      }
    }
    

    结论或问题:

    
    一个基本的Pormise,不过我们上面都是基于成功fulfilled状态的实现
    下面我们来增加错误捕获
    

    尝试五(错误捕获)

    classess/Promise5.php

    
    class Promise5
    {
        private $value;
        private $reason;
        private $state;
        private $fulfilledCallbacks = [];
        private $rejectedCallbacks = [];
    
        public function __construct(Closure $func = null)
        {
            $this->state = PromiseState::PENDING;
    
            $func([$this, 'resolve'], [$this, 'reject']);
        }
    
        public function then(Closure $onFulfilled = null, Closure $onRejected = null)
        {
            // 此处作用是兼容then方法的以下四种参数变化,catchError就是第二种情况
            // 1. then($onFulfilled, null)
            // 2. then(null, $onRejected)
            // 3. then(null, null)
            // 4. then($onFulfilled, $onRejected)
            $onFulfilled = is_callable($onFulfilled) ? $onFulfilled :  function ($value) {return $value;};
            $onRejected = is_callable($onRejected) ? $onRejected :  function ($reason) {throw $reason;};
    
            $thenPromise = new Promise5(function ($reslove, $reject) use (&$thenPromise, $onFulfilled, $onRejected) {
    
                //$this 代表的当前的Promise对象,不要混淆了
    
                // 如果是异步回调,状态未变化之前,then的回调方法压入相应的数组方便后续调用
                if ($this->state == PromiseState::PENDING) {
                    $this->fulfilledCallbacks[] = static function() use ($thenPromise, $onFulfilled, $reslove, $reject){
                        try {
                            $value = $onFulfilled($this->value);
                            $this->resolvePromise($thenPromise, $value, $reslove, $reject);
                        } catch (Exception $e) {
                            $reject($e);
                        }
                    };
    
                    $this->rejectedCallbacks[] = static function() use ($thenPromise, $onRejected, $reslove, $reject){
                        try {
                            $reason = $onRejected($this->reason);
                            $this->resolvePromise($thenPromise, $reason, $reslove, $reject);
                        } catch (Exception $e) {
                            $reject($e);
                        }
                    };
                }
    
                // 如果状态是fulfilled,直接回调执行并传参value
                if ($this->state == PromiseState::FULFILLED) {
                    try {
                        $value = $onFulfilled($this->value);
                        $this->resolvePromise($thenPromise, $value, $reslove, $reject);
                    } catch (Exception $e) {
                        $reject($e);
                    }
                }
    
                // 如果状态是rejected,直接回调执行并传参reason
                if ($this->state == PromiseState::REJECTED) {
                    try {
                        $reason = $onRejected($this->reason);
                        $this->resolvePromise($thenPromise, $reason, $reslove, $reject);
                    } catch (Exception $e) {
                        $reject($e);
                    }
                }
    
            });
    
            // 返回对象自身,实现链式调用
            return $thenPromise;
    
        }
    
        public function catchError($onRejected)
        {
            return $this->then(null, $onRejected);
        }
    
        /**
         * 解决Pormise链式then传递
         * 可参考 [Promises/A+]2.3 [https://promisesaplus.com/#the-promise-resolution-procedure]
         * @param $thenPromise
         * @param $x            $x为thenable对象
         * @param $resolve
         * @param $reject
         */
        private function resolvePromise($thenPromise, $x, $resolve, $reject)
        {
            $called = false;
    
            if ($thenPromise === $x) {
                return $reject(new Exception('循环引用'));
            }
    
            if ( is_object($x) && method_exists($x, 'then')) {
                try {
                    $resolveCb = function ($value) use ($thenPromise, $resolve, $reject, $called) {
                        if ($called) return;
                        $called = true;
                        // 成功值y有可能还是promise或者是具有then方法等,再次resolvePromise,直到成功值为基本类型或者非thenable
                        $this->resolvePromise($thenPromise, $value, $resolve, $reject);
                    };
    
                    $rejectCb = function ($reason) use ($thenPromise, $resolve, $reject, $called) {
                        if ($called) return;
                        $called = true;
                        $reject($reason);
                    };
    
                    call_user_func_array([$x, 'then'], [$resolveCb, $rejectCb]);
                } catch (Exception $e) {
                    if ($called) return ;
                    $called = true;
                    $reject($e);
                }
    
            } else {
                if ($called) return ;
                $called = true;
                $resolve($x);
            }
        }
    
        /**
         * 执行回调方法里的resolve绑定的方法
         * 本状态只能从pending->fulfilled
         * @param null $value
         */
        public function resolve($value = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::FULFILLED;
                $this->value = $value;
    
                array_walk($this->fulfilledCallbacks, function ($callback) {
                    $callback(); //因为回调本身携带了作用于,所以直接调用,无法参数
                });
            }
        }
    
        /**
         * 执行回调方法里的rejected绑定的方法
         * 本状态只能从pending->rejected
         * @param null $reason
         */
        public function reject($reason = null)
        {
            if ($this->state == PromiseState::PENDING) {
                $this->state = PromiseState::REJECTED;
                $this->reason = $reason;
    
                array_walk($this->rejectedCallbacks, function ($callback) {
                    $callback(); //因为回调本身携带了作用于,所以直接调用,无法参数
                });
            }
        }
    
        public function getState()
        {
            return $this->state;
        }
    
        public function getValue()
        {
            return $this->value;
        }
    
        public function getReason()
        {
            return $this->reason;
        }
    }
    

    promise5.php

    
    require "autoload.php";
    
    $promise1 = new Promise5(function($resolve, $reject) {
        $resolve("打印我");
    });
    
    $promise2 = $promise1->then(function ($value) {
        var_dump($value);
        throw new Exception("promise2 error");
        return "promise2";
    }, function ($reason) {
        var_dump($reason->getMessage());
        return "promise3 error return";
    });
    
    //我们可以简写then方法,只传入$onFulfilled方法,然后错误会自己冒泡方式到下一个catchError或then里处理。
    //$promise3 = $promise2->then(function ($value) {
    //    var_dump($value);
    //    return new Promise5(function($resolve, $reject) {
    //        $resolve("promise3");
    //    });
    //})->catchError(function ($reason) {
    //    var_dump($reason->getMessage());
    //    return "promise3 error return";
    //});
    
    $promise3 = $promise2->then(function ($value) {
        var_dump($value);
        return new Promise5(function($resolve, $reject) {
            $resolve("promise3");
        });
    }, function ($reason) {
        var_dump($reason->getMessage());
        return "promise3 error return";
    });
    
    $promise4 = $promise3->then(function ($value) {
        var_dump($value);
        return "promise4";
    }, function ($reason) {
        echo $reason->getMessage();
    });
    
    var_dump($promise4);
    

    结果:

    
    string(9) "打印我"
    string(14) "promise2 error"
    string(21) "promise3 error return"
    object(Promise4)#10 (5) {
      ["value":"Promise4":private]=>
      string(8) "promise4"
      ["reason":"Promise4":private]=>
      NULL
      ["state":"Promise4":private]=>
      string(9) "fulfilled"
      ["fulfilledCallbacks":"Promise4":private]=>
      array(0) {
      }
      ["rejectedCallbacks":"Promise4":private]=>
      array(0) {
      }
    }
    

    结论或问题:

    
    这里我们基础实现了一个可以用于生产环境的Promise
    后续我们会接续完善这个Promise的特有方法,比如:finally, all, race, resolve, reject等
    后续再介绍用Promise实现的自动执行器等
    

    附录参考

    Promises/A+
    Promises/A+ 中文
    Promise 对象 - ECMAScript 6 入门 阮一峰

    原文地址:https://segmentfault.com/a/1190000016564649

  • 相关阅读:
    jquery的$().each,$.each的区别
    前端面试题整理
    JS中Null与Undefined的区别
    LESS介绍及其与Sass的差异(转载自伯乐在线,原文链接:http://blog.jobbole.com/24671/)
    APP 弱网测试
    ADB命令
    pytest之参数化parametrize的使用
    APP测试
    python 异常捕捉
    pip 安装依赖 requirements.txt
  • 原文地址:https://www.cnblogs.com/lalalagq/p/9970006.html
Copyright © 2011-2022 走看看