资讯专栏INFORMATION COLUMN

PHP下的异步尝试四:PHP版的Promise

lentoo / 798人阅读

摘要:结果打印我结论或问题这里我们基础实现了一个可以用于生产环境的后续我们会接续完善这个的特有方法,比如等后续再介绍用实现的自动执行器等附录参考中文对象入门阮一峰

PHP下的异步尝试系列

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

PHP下的异步尝试一:初识生成器

PHP下的异步尝试二:初识协程

PHP下的异步尝试三:协程的PHP版thunkify自动执行器

PHP下的异步尝试四:PHP版的Promise

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

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

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://www.ucloud.cn/yun/29463.html

相关文章

  • PHP下的异步尝试二:初识协程

    摘要:如果仅依靠程序自动交出控制的话,那么一些恶意程序将会很容易占用全部时间而不与其他任务共享。多个操作可以在重叠的时间段内进行。 PHP下的异步尝试系列 如果你还不太了解PHP下的生成器,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify自动执行器 PHP下的异步尝试四:PHP版的Promise ...

    MudOnTire 评论0 收藏0
  • PHP下的异步尝试三:协程的PHP版thunkify自动执行器

    摘要:函数并不是生成器协程函数自动执行的唯一方案。因为自动执行的关键是,必须有一种机制,自动控制生成器协程函数的流程,接收和交还程序的执行权。回调函数可以做到这一点,对象也可以做到这一点。本系列的下一篇,将介绍基于的实现的自动执行器。 PHP下的异步尝试系列 如果你还不太了解PHP下的生成器和协程,你可以根据下面目录翻阅 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 P...

    wudengzan 评论0 收藏0
  • PHP下的异步尝试一:初识生成器

    摘要:下的异步尝试系列下的异步尝试一初识生成器下的异步尝试二初识协程下的异步尝试三协程的版自动执行器下的异步尝试四版的下的异步尝试五版的的继续完善生成器类获取迭代器当前值获取迭代器当前值返回当前产生的键生成器从上一次处继续执行重置迭代器向生成器中 PHP下的异步尝试系列 PHP下的异步尝试一:初识生成器 PHP下的异步尝试二:初识协程 PHP下的异步尝试三:协程的PHP版thunkify自...

    tomorrowwu 评论0 收藏0
  • nodejs异步编程详解

    摘要:四异步编程解决方案模式模式一定程度上缓解了嵌套回调的问题,只会处在未完成完成态失败态中的一种,只会从未完成转化为完成态或者失败态,不能逆转。 一、从一个简单的案例开始 fs.readdir(path.join(__dirname, ./index.js), (err, files) => { files.foreach((filename, index) => { ...

    inapt 评论0 收藏0
  • promise初识

    摘要:一涉及技术二简介官方文档三种状态进行中已完成,又称已失败只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 一、涉及技术 jquery、vue、php 二、Promise简介 MDN官方文档:Promise 三种状态: Pending(进行中) Resolved(已完成,又称 Fulfilled) Rejected(已失败) 只有异步操作的结果,可以决定...

    codercao 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<