es6 队列_ES6 Promise个人理解

本文所有JS执行结果基于Chrome浏览器83.0.4103.61版本得出,如果在其他浏览器上出现不同执行结果,那不关我事

Promise的用法示范

var p = new Promise((resolve, reject) => {

var xhr = new XMLHttpRequest();

xhr.open('GET', '/');

xhr.onreadystatechange = function() {

if (xhr.readyState === xhr.DONE)

if (xhr.status === 200)

resolve(xhr);

else

reject();

};

xhr.onerror = reject;

xhr.send();

});

p.then((xhr) => {

console.log(xhr);

return xhr.response;

}).then((resp) => {

console.log(resp);

}).catch(() => {

console.error('Failed');

}).finally(() => {

console.log('Done');

});

Promise类的构造函数的参数被称为Resolver(也叫executor),用于定义具体的异步操作,Promise对象会向Resolver函数传入resolve与reject两个参数,用于在异步操作执行成功与失败时调用。

resolve与reject两个函数都可以接受一个参数以供Promise对象后续使用。

当Resolver执行过程中出现异常时,Promise对象会自动调用reject函数。

Promise对象的状态

pending:

从Promise对象初始化到resolve或reject被调用之前,Promise对象会处于此状态。

resolved(fulfilled):

当Resolver中调用了resolve函数时,Promise会进入此状态并执行相应后续操作。

rejected:

当Resolver中调用了reject函数时,Promise会进入此状态并执行相应后续操作。

Promise对象的方法

then(onresolved, onrejected):

then是Promise对象的一个核心方法,用于定义异步操作的后续操作。

当Promise对象进入resolved状态时,它会执行onresolved参数所定义的函数。

当Promise对象进入rejected状态时,它会执行onrejected参数所定义的函数。

onresolved与onrejected两个函数都可以接受一个参数,这个参数对应Resolver向resolve与reject函数传入的参数

catch(onrejected):

catch是then的一个简写,调用catch(onrejected)等同于调用then(null, onrejected)

finally(onfinally):

finally是无论Promise对象是resolved状态还是rejected状态,都会去调用onfinally,约等于then(onfinally, onfinally),与then的区别在于onfinally不会接收参数,onfinally的return也不会传递给后面的then

等等,finally后面的then是什么鬼?

其实finally之后依然可以then、catch、finally,写起来就变成了p.finally(...).then(...).then(...)……怎么想都很奇怪,而且finally后面的then获取的参数是finally之前的返回值

Promise核心部分

上面的东西估计随便搜一下Promise就能搜着,下面的这些则是我在ES5中造轮子写Promise时研究出的一些东西。

1.

Resolver会在Promise构造函数中直接执行

验证代码:

new Promise(() => console.log(1));

console.log(2);

结果:

1

2

2.

Promise对象的链式调用then与finally返回的并不是this,而是一个根据其参数生成的新Promise对象

这些对象以单向链表的形式连接,最终会形成一棵树

后面称由then与finally生成的对象为“子对象”,被调用的对象为“主对象”,其中then的两个参数称为“状态函数”,finally方法的参数不属于状态函数

子对象刚创建时是pending状态

验证then返回不同Promise对象:

var p = new Promise(r => r());

var pp = p.then(() => {

console.log(p);

console.log(pp);

console.log(p===pp);

});

结果:

Promise {: undefined}

Promise {}

false

3.

Resolver中的参数resolve与reject一共只能调用一次,因为只有Promise对象是在pending状态时,resolve与reject才能修改Promise对象的状态与值,而只有在Promise对象的状态发生改变时,主对象才会开始调用子对象的状态函数

验证代码:

var funcs = {};

var p = new Promise((resolve, reject) => {

funcs.resolve = resolve;

funcs.reject = reject;

});

p.then(()=>console.log('res'), ()=>console.log('rej'));

console.log(p);

funcs.resolve(1);

console.log(p);

funcs.reject(2);

console.log(p);

结果

Promise {}

Promise {: 1}

Promise {: 1}

res

4.

上面的结果显示console.log('res')晚于代码中的最后一句console.log(p)执行,其实Promise与setTimeout类似,会把主对象调用子对象状态函数的过程推迟到空闲时执行,防止主线程里一堆then、finally还没执行完,甚至可能构造函数都还没执行完就开始调用子对象的情况出现

虽然上面说与setTimeout类似,但主对象调用子对象的过程永远早于同一时刻所有的setTimeout,推测setInterval也是同理

所以可以把Promise.resolve(value).then(handle)当作最高优先级的setTimeout(handle, 0, value)来使用

注:

Promise.resolve(value)可以理解为new Promise(resolve => resolve(value));

Promise.reject(value)可以理解为new Promise((resolve, reject) => reject(value));

验证代码:

setTimeout(console.log, 0, 0);

new Promise(r => r()).then(z=>console.log(1));

Promise.resolve().then(z=>console.log(2));

setTimeout(console.log, 0, 4);

console.log(5);

结果

5

1

2

0

4

5.

当子对象拥有主对象状态对应的状态函数时,主对象会直接调用此状态函数,如果成功执行,子对象状态变为resolved,值为状态函数返回值;如果抛出异常,子对象状态变为rejected,值为异常对象

如果子对象没有主对象状态对应的状态函数,则会直接继承主对象的状态与值,交由更后面的子对象处理

如果最后一个子对象执行后依然是rejected状态,浏览器会抛出带有(in promise)字样的异常

验证代码:

var p = Promise.reject(1);

var pp = p.then(()=>console.log(pp)).then(()=>console.log(pp));

pp.finally(()=>console.log(pp));

结果:

Promise {: 1}

Promise {: 1}

Uncaught (in promise) 1

验证代码:

var p = Promise.resolve(1);

var pp = p.catch(()=>console.log(pp)).catch(()=>console.log(pp));

pp.finally(()=>console.log(pp));

结果:

Promise {: 1}

Promise {: 1}

6.

为防止抛出异常干扰其他Promise对象执行,浏览器会把异常留到最后抛出

验证代码:

var p = Promise.reject();

p.then(()=>console.log(1));

p.catch(()=>console.log(2)).then(()=>console.log(3)).then(()=>console.log(4));

结果:

2

3

4

Uncaught (in promise) undefined

7.

主对象使得子对象的状态发生了改变之后,子对象也会开始准备调用其自身的子对象,同样地,它会被推迟到空闲时执行

如果把Promise对象极其子对象们构成的树看成一个单向图,会发现它的执行顺序与广度优先搜索算法BFS是一致的

验证代码:

var p = Promise.resolve();

p.then(() => {console.log(1);}).then(() => {console.log(2);});

p.then(() => {console.log(3);}).then(() => {console.log(4);});

结果:

1

3

2

4

这里需要来个难理解一点的例子加深一下记忆:

var x = function() {

var xp = Promise.resolve();

xp.then(() => {console.log(1);}).then(() => {console.log(2);});

xp.then(() => {console.log(3);}).then(() => {console.log(4);});

}

var p = Promise.resolve();

p.then(x).then(() => {console.log(5);});

p.then(() => {console.log(6);}).then(() => {console.log(7);});

它的结果为:

6

1

3

5

7

2

4

如果最后两句对调,变成这样:

var x = function() {

var xp = Promise.resolve();

xp.then(() => {console.log(1);}).then(() => {console.log(2);});

xp.then(() => {console.log(3);}).then(() => {console.log(4);});

}

var p = Promise.resolve();

p.then(() => {console.log(6);}).then(() => {console.log(7);});

p.then(x).then(() => {console.log(5);});

结果为:

6

7

1

3

5

2

4

为什么是这个顺序?请先自己思考一下。

解析:

设有这么个队列,它决定了下次要调用子对象状态函数的Promise对象,就叫它“执行队列”好了

再设执行队列中所有对象的子对象按顺序可组成一个新的队列,就叫它“子对象队列”吧

第一个例子:

第一次推迟:

刚一开始,执行队列如下:

[p]

子对象队列如下:

[then(x), then(log(6))]

开始依次执行子对象队列中对象的相应状态函数

then(x)调用了x,x执行时创建了xp,xp是resolved状态,进入执行队列

x 执行结束后,then(x)自身进入执行队列

then(log(6))输出了6,自身进入执行队列

第二次推迟:

执行队列:

[xp, then(x), then(log(6))]

子对象队列:

[then(log(1)), then(log(3)), then(log(5)), then(log(7))]

依次输出了1、3、5、7,子对象队列中的对象依次进入执行队列

第三次推迟:

执行队列:

[then(log(1)), then(log(3)), then(log(5)), then(log(7))]

子对象队列:

[then(log(2)), then(log(4))]

依次输出2、4,子对象队列中的对象依次进入执行队列

第四次推迟:

执行队列:

[then(log(2)), then(log(4))]

子对象队列是空的,执行个锤子

第二个例子也就换换顺序,不分析了

8.

如果状态函数返回一个Promise对象,那么状态函数所在的Promise对象的所有子对象都会转移至返回的对象上

验证代码:

Promise.resolve().then(() => {

console.time('Promise');

}).then(() => {

return new Promise(r => setTimeout(r, 1000, 123123));

}).then(v => {

console.timeEnd('Promise');

console.log(v);

});

结果:

Promise: 1000.989990234375ms

123123

End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值