新建一个
promise很简单,只需要
new一个
promise对象即可。所以
promise本质上就是一个函数,它接受一个函数作为参数,并且会返回
promise对象,这就给链式调用提供了基础
其实
Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
pending: 初始状态,位履行或拒绝
fulfilled: 意味着操作成功完成
rejected: 意味着操作失败
pending 状态的
Promise对象可能以
fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(
reject)了。当其中任一种情况出现时,
Promise 对象的
then 方法绑定的处理方法(handlers)就会被调用,then方法分别指定了
resolve方法和
reject方法的回调函数
上面代码中,
resolve方法和
reject方法调用时,都带有参数。它们的参数会被传递给回调函数。
reject方法的参数通常是
Error对象的实例,而
resolve方法的参数除了正常的值以外,还可能是另一个
Promise实例,比如像下面这样。
上面代码中,
p1和
p2都是
Promise的实例,但是
p2的
resolve方法将
p1作为参数,这时
p1的状态就会传递给
p2。如果调用的时候,
p1的状态是
pending,那么
p2的回调函数就会等待
p1的状态改变;如果
p1的状态已经是
fulfilled或者
rejected,那么
p2的回调函数将会立刻执行
Promise.prototype.catch方法是
Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个
catch语句捕获
Promise.all方法用于将多个
Promise实例,包装成一个新的
Promise实例
上面代码中,
Promise.all方法接受一个数组作为参数,
p1、
p2、
p3都是
Promise对象的实例。(
Promise.all方法的参数不一定是数组,但是必须具有
iterator接口,且返回的每个成员都是
Promise实例。)
p的状态由
p1、
p2、
p3决定,分成两种情况
只有
p1、
p2、
p3的状态都变成
fulfilled,
p的状态才会变成
fulfilled,此时
p1、
p2、
p3的返回值组成一个数组,传递给
p的回调函数
只要
p1、
p2、
p3之中有一个被
rejected,
p的状态就变成
rejected,此时第一个被
reject的实例的返回值,会传递给p的回调函数
Promise.race方法同样是将多个
Promise实例,包装成一个新的
Promise实例。
上面代码中,只要
p1、
p2、
p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值
如果
Promise.all方法和
Promise.race方法的参数,不是
Promise实例,就会先调用下面讲到的
Promise.resolve方法,将参数转为
Promise实例,再进一步处理
有时需要将现有对象转为
Promise对象,
Promise.resolve方法就起到这个作用
上面代码将
jQuery生成
deferred对象,转为一个新的
ES6的
Promise对象
如果
Promise.resolve方法的参数,不是具有
then方法的对象(又称
thenable对象),则返回一个新的
Promise对象,且它的状态为
fulfilled。
上面代码生成一个新的
Promise对象的实例
p,它的状态为
fulfilled,所以回调函数会立即执行,
Promise.resolve方法的参数就是回调函数的参数
如果
Promise.resolve方法的参数是一个
Promise对象的实例,则会被原封不动地返回
Promise.reject(reason)方法也会返回一个新的
Promise实例,该实例的状态为
rejected。
Promise.reject方法的参数
reason,会被传递给实例的回调函数
async/await 是基于
Promise 的,因为使用
async 修饰的方法最终返回一个
Promise, 实际上,
async/await 可以看做是使用
Generator 函数处理异步的语法糖,我们来看看如何使用
Generator 函数处理异步
上面的代码,我们逐步调用遍历器的
next() 方法,由于每一个
next() 方法返回值的
value 属性为一个
Promise 对象,所以我们为其添加
then 方法, 在
then方法里面接着运行
next 方法挪移遍历器指针,直到
Generator 函数运行完成,实际上,这个过程我们不必手动完成,可以封装成一个简单的执行器
run方法用来自动运行异步的
Generator 函数,其实就是一个递归的过程调用的过程。这样我们就不必手动执行
Generator 函数了。 有了
run 方法,我们只需要这样运行 getData 方法
这样,我们就可以把异步操作封装到
Generator 函数内部,使用
run 方法作为
Generator 函数的自执行器,来处理异步。其实我们不难发现,
async/await 方法相比于
Generator 处理异步的方式,有很多相似的地方,只不过
async/await 在语义化方面更加明显,同时
async/await 不需要我们手写执行器,其内部已经帮我们封装好了,这就是为什么说
async/await 是
Generator 函数处理异步的语法糖了
Promise 规范有很多,如
Promise/A,
Promise/B,
Promise/D以及
Promise/A 的升级版
Promise/A+。
ES6中采用了
Promise/A+ 规范
一个
promise的当前状态只能是
pending、
fulfilled和
rejected三种之一。状态改变只能是
pending到
fulfilled或者
pending到
rejected。状态改变不可逆
promise的
then方法接收两个可选参数,表示该
promise状态改变时的回调(
promise.then(onFulfilled, onRejected))。
then方法返回一个
promise。
then 方法可以被同一个
promise 调用多次
调用
then方法,将想要在
Promise异步操作成功时执行的回调放入
callbacks队列,其实也就是注册回调函数,可以向观察者模式方向思考
创建
Promise实例时传入的函数会被赋予一个函数类型的参数,即
resolve,它接收一个参数
value,代表异步操作返回的结果,当一步操作执行成功后,用户会调用
resolve方法,这时候其实真正执行的操作是将
callbacks队列中的回调一一执行
结合例1中的代码来看,首先
new Promise时,传给
promise的函数发送异步请求,接着调用
promise对象的
then属性,注册请求成功的回调函数,然后当异步请求发送成功时,调用
resolve(results.id)方法, 该方法执行
then方法注册的回调数组
then方法应该能够链式调用,但是上面的最基础简单的版本显然无法支持链式调用。想让
then方法支持链式调用,其实也是很简单的
上述代码可能还存在一个问题:如果在
then方法注册回调之前,
resolve函数就执行了,怎么办?比如
promise内部的函数是同步函数
这显然是不允许的,
Promises/A+规范明确要求回调需要通过异步方式执行,用以保证一致可靠的执行顺序。因此我们要加入一些处理,保证在
resolve执行之前,
then方法已经注册完所有的回调。我们可以这样改造下
resolve函数:
上述代码的思路也很简单,就是通过
setTimeout机制,将
resolve中执行回调的逻辑放置到
JS任务队列末尾,以保证在
resolve执行时,
then方法的回调函数已经注册完成
但是,这样好像还存在一个问题,可以细想一下:如果
Promise异步操作已经成功,这时,在异步操作成功之前注册的回调都会执行,但是在
Promise异步操作成功这之后调用的
then注册的回调就再也不会执行了,这显然不是我们想要的
我们必须加入状态机制,也就是大家熟知的
pending、
fulfilled、
rejected
Promises/A+规范中的
2.1 Promise States中明确规定了,
pending可以转化为
fulfilled或
rejected并且只能转化一次,也就是说如果
pending转化到
fulfilled状态,那么就不能再转化到r
ejected。并且
fulfilled和
rejected状态只能由
pending转化而来,两者之间不能互相转换
上述代码的思路是这样的:
resolve执行时,会将状态设置为
fulfilled,在此之后调用
then添加的新回调,都会立即执行
如果用户在
then函数里面注册的仍然是一个
Promise,该如何解决?比如下面的例4
这种场景相信用过
promise的人都知道会有很多,那么类似这种就是所谓的链式
Promise
链式
Promise是指在当前
promise达到
fulfilled状态后,即开始进行下一个
promise(后邻
promise)。那么我们如何衔接当前
promise和后邻
promise呢?(这是这里的难点
只要在
then方法里面
return一个
promise就好啦。
Promises/A+规范中的
2.2.7就是这样
then方法中,创建并返回了新的
Promise实例,这是串行
Promise的基础,并且支持链式调用
handle方法是
promise内部的方法。
then方法传入的形参
onFulfilled以及创建新
Promise实例时传入的
resolve均被
push到当前
promise的
callbacks队列中,这是衔接当前
promise和后邻
promise的关键所在
getUserId生成的
promise(简称
getUserId promise)异步操作成功,执行其内部方法
resolve,传入的参数正是异步操作的结果
id
调用
handle方法处理
callbacks队列中的回调:
getUserJobById方法,生成新的
promise(g
etUserJobById promise)
执行之前由
getUserId promise的
then方法生成的新
promise(称为
bridge promise)的
resolve方法,传入参数为
getUserJobById promise。这种情况下,会将该
resolve方法传入
getUserJobById promise的
then方法中,并直接返回
在
getUserJobById promise异步操作成功时,执行其
callbacks中的回调:
getUserId bridge promise中的
resolve方法
最后执行
getUserId bridge promise的后邻
promise的
callbacks中的回调
上述代码增加了新的
reject方法,供异步操作失败时调用,同时抽出了
resolve和
reject共用的部分,形成
execute方法
错误冒泡是上述代码已经支持,且非常实用的一个特性。在
handle中发现没有指定异步操作失败的回调时,会直接将
bridge promise(
then函数返回的
promise,后同)设为
rejected状态,如此达成执行后续失败回调的效果。这有利于简化串行Promise的失败处理成本,因为一组异步操作往往会对应一个实际功能,失败处理方法通常是一致的
如果在执行成功回调、失败回调时代码出错怎么办?对于这类异常,可以使用
try-catch捕获错误,并将
bridge promise设为
rejected状态。
handle方法改造如下
如果在异步操作中,多次执行
resolve或者
reject会重复处理后续回调,可以通过内置一个标志位解决
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 三种状态</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">const</span> PENDING =
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"pending"</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">const</span> RESOLVED =
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"resolved"</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">const</span> REJECTED =
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"rejected"</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// promise 接收一个函数参数,该函数会立即执行</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span>
<span class="title" style="box-sizing: border-box;">MyPromise</span>(
<span class="params" style="box-sizing: border-box;">fn</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">let</span> _this =
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">this</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.currentState = PENDING;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.value =
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">undefined</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 用于保存 then 中的回调,只有当 promise</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 状态为 pending 时才会缓存,并且每个实例至多缓存一个</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.resolvedCallbacks = [];</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.rejectedCallbacks = [];</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.resolve =
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">value</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (value
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">instanceof</span> MyPromise) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 如果 value 是个 Promise,递归执行</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span> value.then(_this.resolve, _this.reject)</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> setTimeout(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">()</span> =></span> {
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 异步执行,保证执行顺序</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (_this.currentState === PENDING) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.currentState = RESOLVED;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.value = value;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.resolvedCallbacks.forEach(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">cb</span> =></span> cb());</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> })</span>
<br>
<span class="line" style="box-sizing: border-box;"> };</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.reject =
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">reason</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;"> setTimeout(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">()</span> =></span> {
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 异步执行,保证执行顺序</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (_this.currentState === PENDING) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.currentState = REJECTED;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.value = reason;</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.rejectedCallbacks.forEach(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">cb</span> =></span> cb());</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> })</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 用于解决以下问题</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// new Promise(() => throw Error(error))</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;"> fn(_this.resolve, _this.reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (e) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> _this.reject(e);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">}</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;">MyPromise.prototype.then =
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">onResolved, onRejected</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> self =
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">this</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.2.7,then 必须返回一个新的 promise</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> promise2;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.2.onResolved 和 onRejected 都为可选参数</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 如果类型不是函数需要忽略,同时也实现了透传</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// Promise.resolve(4).then().then((value) => console.log(value))</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">typeof</span>
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">function</span> ? onResolved :
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">v</span> =></span> v;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">typeof</span>
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">function</span> ? onRejected :
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="params" style="box-sizing: border-box;">r</span> =></span>
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">throw</span> r;</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (self.currentState === RESOLVED) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span> (promise2 =
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">new</span> MyPromise(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">resolve, reject</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.2.4,保证 onFulfilled,onRjected 异步执行</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 所以用了 setTimeout 包裹下</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> setTimeout(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;"></span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> x = onResolved(self.value);</span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, x, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (reason) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(reason);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> });</span>
<br>
<span class="line" style="box-sizing: border-box;"> }));</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (self.currentState === REJECTED) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span> (promise2 =
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">new</span> MyPromise(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">resolve, reject</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;"> setTimeout(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;"></span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 异步执行onRejected</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> x = onRejected(self.value);</span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, x, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (reason) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(reason);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> });</span>
<br>
<span class="line" style="box-sizing: border-box;"> }));</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (self.currentState === PENDING) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span> (promise2 =
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">new</span> MyPromise(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">resolve, reject</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;"> self.resolvedCallbacks.push(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;"></span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 考虑到可能会有报错,所以使用 try/catch 包裹</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> x = onResolved(self.value);</span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, x, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (r) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(r);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> });</span>
<br>
<span class="line" style="box-sizing: border-box;"></span>
<br>
<span class="line" style="box-sizing: border-box;"> self.rejectedCallbacks.push(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;"></span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">var</span> x = onRejected(self.value);</span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, x, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (r) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(r);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> });</span>
<br>
<span class="line" style="box-sizing: border-box;"> }));</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">};</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span>
<span class="title" style="box-sizing: border-box;">resolutionProcedure</span>(
<span class="params" style="box-sizing: border-box;">promise2, x, resolve, reject</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.1,x 不能和 promise2 相同,避免循环引用</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (promise2 === x) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span> reject(
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">new</span>
<span class="built_in" style="box-sizing: border-box; color: rgb(26, 188, 156);">TypeError</span>(
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"Error"</span>));</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.2</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 如果 x 为 Promise,状态为 pending 需要继续等待否则执行</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (x
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">instanceof</span> MyPromise) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (x.currentState === PENDING) {</span>
<br>
<span class="line" style="box-sizing: border-box;"> x.then(
<span class="function" style="box-sizing: border-box; color: rgb(82, 82, 82);">
<span class="keyword" style="box-sizing: border-box; color: rgb(0, 146, 219);">function</span> (
<span class="params" style="box-sizing: border-box;">value</span>) </span>{</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 再次调用该函数是为了确认 x resolve 的</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 参数是什么类型,如果是基本类型就再次 resolve</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 把值传给下个 then</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, value, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">else</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;"> x.then(resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.3.3</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// reject 或者 resolve 其中一个执行过得话,忽略其他的</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">let</span> called =
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">false</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3,判断 x 是否为对象或者函数</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (x !==
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">null</span> && (
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">typeof</span> x ===
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"object"</span> ||
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">typeof</span> x ===
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"function"</span>)) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.2,如果不能取出 then,就 reject</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">try</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.1</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">let</span> then = x.then;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 如果 then 是函数,调用 x.then</span></span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">typeof</span> then ===
<span class="string" style="box-sizing: border-box; color: rgb(26, 188, 156);">"function"</span>) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.3</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> then.call(</span>
<br>
<span class="line" style="box-sizing: border-box;"> x,</span>
<br>
<span class="line" style="box-sizing: border-box;"> y => {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (called)
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> called =
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">true</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.3.1</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> resolutionProcedure(promise2, y, resolve, reject);</span>
<br>
<span class="line" style="box-sizing: border-box;"> },</span>
<br>
<span class="line" style="box-sizing: border-box;"> e => {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (called)
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> called =
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">true</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(e);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> );</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">else</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.3.4</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> resolve(x);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">catch</span> (e) {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">if</span> (called)
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">return</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> called =
<span class="literal" style="box-sizing: border-box; color: rgb(174, 129, 255);">true</span>;</span>
<br>
<span class="line" style="box-sizing: border-box;"> reject(e);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;"> }
<span class="keyword" style="box-sizing: border-box; color: rgb(233, 105, 0);">else</span> {</span>
<br>
<span class="line" style="box-sizing: border-box;">
<span class="comment" style="box-sizing: border-box; color: rgb(179, 179, 179);">// 规范 2.3.4,x 为基本类型</span></span>
<br>
<span class="line" style="box-sizing: border-box;"> resolve(x);</span>
<br>
<span class="line" style="box-sizing: border-box;"> }</span>
<br>
<span class="line" style="box-sizing: border-box;">}</span>
<br>
通过
Promise.prototype.then和
Promise.prototype.catch方法将观察者方法注册到被观察者
Promise对象中,同时返回一个新的
Promise对象,以便可以链式调用
被观察者管理内部
pending、
fulfilled和
rejected的状态转变,同时通过构造函数中传递的
resolve和
reject方法以主动触发状态转变和通知观察者