ES6的Promise和jQuery的Deferred对象很像,之前在 $.Deferred源码分析也讲过关于Deferred的分析。不过Promise和Deferred还是有些许差异。今天就主要分析Promise,看看主要的运行原理。

基本结构

一个Promise对象中包含status(状态:pending,resolved,rejected),value(当前的值),onResolve队列,onReject队列:

提供的resolve方法和reject方法可以执行onResolve队列和onReject队列,由此我们可以画出一个Promise的基本结构:

background Layer 1 value: onResolve:[] onReject:[] status:pending resolve reject

我们可以把一个Promise设想成一个炮竹,然后resolve和reject方法想象成两条引线,任何一个方法都可以引爆当前Promise。引爆后value就得到值,然后这个Promise就废了。

background Layer 1 value:‘ok' status:resolved

上面是我们执行了resolved('ok')之后的Promise模型,value得到了'ok',但是丧失了resolve和reject方法以及队列。

then方法

Promise的then会干两件事,第一是安插/执行方法,第二是返回一个新的Promise:

如果当前Promise的状态是pending,那么表示此时还未执行完,将then中的方法放入onResolve和onReject集合:

background Layer 1 value: status:pending new Promise((resolve,reject)=>{}).then(f1,f2) onResolve: onReject: resolve reject f1 f2 value: status:pending onResolve: onReject: resolve reject promise对象 then返回的Promise

对于状态为resolved或者rejected的,then后面的方法会立即执行:

background Layer 1 value:'ok' status:resolved Promise.resolve('ok').then(f1,f2) value: status:pending onResolve: onReject: resolve reject promise对象 then返回的Promise f1 立即执行 value:'no' status:rejected Promise.reject('no').then(f1,f2) value: status:pending onResolve: onReject: resolve reject promise对象 then返回的Promise f2 立即执行

上图可以看到,对于状态为resolved的,会立即执行then后的第一个方法;状态为rejected的,会立即执行then后的第二个方法。

输入输出

在上面的结构中,对于要处理的方法,不管是处于pending中还未执行的方法,还是已经resolved/rejected的需要立即执行的方法。都有一个共同的流程,输入->处理->输出。它们会接受一个输入参数,这个参数为该Promise当前value,处理完后,会返回一个值。

background Layer 1 this.value 处理 返回结果 出错

那么这种输入/输出的结构有啥作用?它是用来引爆下一个Promise的关键,因为我们上面讲到,Promise.then返回的是一个新的Promise,那么这个Promise从和引爆呢?就是从这个方法里面引爆:

background Layer 1 value:'ok' status:resolved Promise.resolve('ok').then(f1,f2) value: status:pending onResolve: onReject: resolve reject promise对象 then返回的Promise 立即执行 ok 结果 出错

Promise.then内部将方法执行的结果作为新Promise的resolve;将方法报错作为新Promise的reject。

多种resolve情况

这个差不多是整个Promise最难懂的部分了,实现的代码如下:

这段代码就是上面我们讲到的方法返回值resolve新Promise的情况,这里把返回值分为三类,一个是Promise对象,一个是含有then的Object,一个是其他类型,前面两种可以归为一类,就是带有then的Object。它们的处理方式是不同的。

先来看一个最简单的,返回是不带then的Object结果:

background Layer 1 new Promise((resolve,reject)=>{ value: status:pending onResolve: onReject: resolve reject promise对象 then返回的Promise resolve(10);return 15; }).then(value=>return 20;) value: status:pending onResolve: onReject: resolve reject resolve(10) value:10 status:resolved onResolve: onReject: 10 20 resolve(20) value:20 status:resolved

上面有一个比较迷惑性的return 15;它其实在这里没有任何作用,因为上面说到方法的输入为this.value,resolve(10),那么this.value就是10,不是15;起作用的是then的返回值,return 20中的20,将以resolve(20)引爆新的Promise。

现在看个稍微复杂一点的,返回的是一个已经resolved的Promise:

background Layer 1 new Promise((resolve,reject)=>{ value: status:pending onResolve: onReject: resolve reject p1 resolve(10); }).then(value=>return p1;).then(value=>console.log(value)); value: status:pending onResolve: onReject: resolve reject resolve(10) value:10 status:resolved onResolve: onReject: 10 p1 p1.then(f1,f2) let p1=Promise.resolve(20); value:20 status:resolved promise对象 value: status:pending onResolve: onReject: resolve reject 第二个then返回的Promise value:undefined status:resolved 第一个then返回的Promise value:20 status:resolved 立即执行f1 resolve(20) value:20 status:resolved onResolve: onReject: 20 undefined resolve(undefined) 20 矫验方法

当我们发现返回值是一个Promise的时候,会调用它的then去执行/安插一个矫验方法,如果状态为resolved/rejected,那么会直接执行这个矫验方法。这个矫验方法的作用很简单,就是验证this.value是不是带有then的Object。这个矫验方法里面包含了下一个Promise的启动方法(resolve,reject),因此只有当矫验方法找到了非then的Object时,才会引爆下一个Promise。

这里的过程有点类似权利的移交,就是当发现返回的是Promise时候,就把下一个Promise的执行权移交到返回的Promise上,由那个Promise决定执行与否。

如果返回的是一个处于pending状态的Promise会怎样:

background Layer 1 new Promise((resolve,reject)=>{ value: status:pending onResolve: onReject: resolve reject p1 resolve(10); }).then(value=>return p1;).then(value=>console.log(value)); value: status:pending onResolve: onReject: resolve reject resolve(10) value:10 status:resolved onResolve: onReject: 10 p1 p1.then(f1,f2) let p1=new Promise((resolve,reject)=>{ promise对象 value: status:pending onResolve: onReject: resolve reject 第二个then返回的Promise value:undefined status:resolved 第一个then返回的Promise resolve(20) value:20 status:resolved onResolve: onReject: 20 undefined resolve(undefined) setTimeout(()=>{resolve(20);},1000); }); value: status:pending onResolve: onReject: resolve reject value: status:pending onResolve: onReject: resolve reject f1 f2 value:20 status:pending onResolve: onReject: 1秒后,resolve(20) 20 执行f1 矫验方法

我们可以发现,如果是处于pending状态的Promise,还是会执行then(f1,f2)方法,只不过此时会把f1,f2放到onResolve/onReject队列中去,等到resolve/reject了,再去执行里面的方法。里面的方法也是矫验方法,验证是否this.value是含then的Object,如果不是,那么执行下一个Promise的resolve,如果是,继续调用then,移交执行权。

现在我们来看看如果返回的是一个带then的Object会是什么样子:

background Layer 1 new Promise((resolve,reject)=>{ value: status:pending onResolve: onReject: resolve reject p1 resolve(10); }).then(value=>return p1;).then(value=>console.log(value)); value: status:pending onResolve: onReject: resolve reject resolve(10) value:10 status:resolved onResolve: onReject: 10 p1 p1.then(f1,f2) let p1={then:function(resolve,reject){ promise对象 value: status:pending onResolve: onReject: resolve reject 第二个then返回的Promise value:undefined status:resolved 第一个then返回的Promise resolve(20) value:20 status:resolved onResolve: onReject: 20 undefined resolve(undefined) resolve(20); })}; then:(resolve,reject)=>{resolve(20);} 20 矫验方法 执行了f1

其他方法

catch方法其实相当于then(null,fn),内部也是调用的then。

Promise.resolve返回的也是一个新的Promise,同时也会矫验传入的参数obj,检查是否是带有then的Object,如果不是,执行resolve,如果是,调用then,移交执行权。

Promise.reject则不用判断,直接reject就好。

Promise.race是多个promise执行,谁先完成谁就结束;promise.all是多个promise执行,但是要记录个数,达到个数才算完成。

Promise.finally内部也是用then去实现的,只不过它有个特性,输出内容=输入内容:

background Layer 1 new Promise((resolve,reject)=>{ value: status:pending onResolve: onReject: resolve reject resolve(10); }).finally(value=>return 15;).then(value=>console.log(value)); value: status:pending onResolve: onReject: resolve reject resolve(10) value:10 status:resolved onResolve: onReject: 10 10 promise对象 value: status:pending onResolve: onReject: resolve reject 第二个then返回的Promise value:undefined status:resolved 第一个then返回的Promise resolve(10) value:10 status:resolved onResolve: onReject: 10 undefined resolve(undefined) console.log位置

上面的return 15依旧没有作用,因为在finally中不会看return 的结果,而是在内部封装,将输入变成输出了,因此这里输出的还是10。

完整代码:

其他文章

0
我要评论

评论

返回
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

提交
还可以输入500个字