Promise的概念在ES6标准推出来之前已经深入人心,很多框架和第三方库都有类似的实现。但在深入理解ES6的Promise对象的时候,受之前经验的影响,很多概念给人似是而非的感觉,其中有一个特别明显的地方就是ES6中对Promise对象状态的定义以及resolved概念。
Promise对象有三个状态:pending,fulfilled,rejected,MDN文档上对Promise对象状态变化过程用一幅图描述:
图中有几个小细节:
fulfilled
,rejected
状态前promise都处在pending
状态。settled
包括了fulfilled
和rejected
。pending
,fulfilled
,rejected
三种状态下切换。事情在现在看来都很美好,状态清晰,一目了然。然而,在各种教程中我们会看到这样一个状态:resolved
,阮老师的ECMAScript 6 入门中直接把它当作了fulfilled
状态:
Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。
但是MDN中的备注是这样写的:
你可能也会听到一个术语
resolved
,它表示Promise对象处于settled
状态,或者Promise对象被锁定在了调用链中。
嗯?记不记得上面提到settled
状态包括fulfilled
和rejected
状态,那么他至少不能和fulfilled
状态划等号吧?于是我又搜了搜,发现网上很多文章提到resolved
这个词时,有人直接当作fulfilled
状态,有人把它理解为complete
(类似地,fulfilled
是success,rejected
自然就是error或者failure),不过更多人都是很含糊地引用了一下文档或者阮老师的文章。
不过确实有人直接提出了这个问题:Promise的fulfill和resolve为啥要用两个词?,我着重看了贺师俊老师的回答:
因为 fulfill 和 resolve 是不同的。
resolve 只是表示这个 promise 已经确定了,比如 promise A 被 resolve 为 promise B,则 A 已经 resolved 但是并未 fulfilled 。
好吧,我承认我一开始看没看懂...
不过还好在备注那里MDN放出了一篇帮助理解的文章,Domenic Denicola 的 States and Fates,基本把这两个概念说清楚了,不过文中只有简单的文字描述,下面说一下我自己的理解。
文章将Promise分得更细,分为了状态(state)和结果(fates,命运,原谅我粗浅的翻译)。
状态(state)就是上面提到的三种状态,需要稍微留意一下的是,文中提到settled
不是一种Promise状态,只是一种语义上的便利,说白了就是描述Promise确定了而已。
结果(fates)分为resolved
和unresolved
,两种结果互斥。
A promise is resolved if trying to resolve or reject it has no effect,当没办法去resolve或reject一个promise的时候,这个promise就是resolved的。
显而易见,只要理解了resolved
的描述,自然就理解unresolved
。首先先提一个Promise的概念,即当Promise的状态一旦改变,就永久保持该状态。所以什么时候没办法去resolve或reject一个promise对象呢?
fulfilled
和rejected
状态的promise对象,因为他们已经无法改变状态,而resolve和reject只能对pending
状态的promise有效。pending
状态的promise对象也有可能处于resolved
,当promise对象被'锁定'在另外一个promise对象或者一个非即时返回的thenable
函数中时,也就是上面贺老师提到的promise A 被 resolve 为 promise B这种情况,为了帮助理解,下面用代码举个例子。说明:Chrome(57.0.2987.133)打印promise对象的时候,[[PromiseStatus]]
有三个值:pending,resolved,rejected,这里的resolved
状态就是指fulfilled
状态,和我们要说明的resolved
没有关系,在firefox(49.0.1)中,[[PromiseStatus]]
三个值和标准相同:pending,fulfilled,rejected。为了避免混淆,用firefox做测试
先上一个简单的代码:
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'p1 resolve');
});
p1.then(value => {
console.log(value); // p1 resolve
console.log(p1); // Promise { : "fulfilled", : "p1 resolve" }
})
很简单,一秒后打印p1 resolve,p1对象状态为fulfilled
。再来看第二例子,在resolve方法中新建了一个Promise对象:
var p1 = new Promise((resolve, reject) => {
function F() {
resolve(new Promise((resolve, reject) => {
console.log(p1); // Promise { : "pending" }
setTimeout(resolve, 2000, 'new Promise resolve');
}))
}
setTimeout(F, 1000, 'p1 resolve');
});
p1.then(function(value) {
console.log(value); // new Promise resolve
console.log(p1); // Promise { : "fulfilled", : "new Promise resolve" }
})
运行一下发现,一秒后打印
Promise { : "pending" }
三秒后打印
new Promise resolve
Promise { : "fulfilled", : "new Promise resolve" }
可以看到,一秒后p1调用了resolve方法,和上一个例子一样被resolve了,理应变为fulfilled
状态,但是他被'锁定'(PS:个人理解锁定:旧promise的结果由新promise决定,类似于promise.resolve(new promise))在了一个新的Promise对象中,所以状态没有立刻改变并执行onfulfilled
方法,而是依然处于pending
状态,但是很明显的是,他的(PS:这里指p1)最终结果(这里可以体会到称为fate的原因,有种宿命的味道)就是fulfilled
状态,此时的p1不能再resolve或reject。再过了两秒后新Promise对象resolve,p1执行then(),状态变为fulfilled
,打印的值也变成了new Promise resolve,而不是p1 resolve。此时打印出的p1的状态就是fulfilled。
(PS:调用了resolve方法,理应变为fulfilled
状态,但是他被'锁定’所以变成了resolved,也就是说
处于pending
状态的promise对象也有可能处于resolved
,例如:当promise对象被'锁定'在另外一个promise对象或者一个非即时返回的thenable
函数中时,也就是上面提到的promise A 被 resolve 为 promise B这种情况,fulfilled已经
实现的,resolved已经解决的)
到此,处于pending
状态的promise对象也有可能处于resolved
情况就很清楚了。
至于unresolved,就是promise可以被resolve或reject的时候,此时promise对象一定处于pending
状态且没有被resolve或reject为其他Promise对象 / 非即时返回的thenable
函数。而反过来,处于pending
状态的promise对象不一定是unresolved。
到这里Promise对象的状态和resolved的概念差不多都清楚了,这篇文章也是我个人理解过程的一个记录。不过参考到的文章不多,文中的解释可能并不是很准确,所以在这里贴出来,希望能看看其他人的理解。
说起来,这篇文章离我上一篇文章已经很久了,真是光阴似箭,岁月......算了,还是不感慨了。现在工作确实没有在学校那会儿充足的时间和精力,总结还是有不少,大部分都在个人的笔记里,没有贴到博客。有些觉得总结份量不够,有些觉得个人的理解不深,不敢随意贴出来。不过在学习一些稍微深入一点的概念的时候,总觉得网上的一些资料并不是很全面和客观,人云亦云的东西太多,自己的理解也是七七八八。所以希望能够贴出来和大家交流,防止自己闭门造车,后续如果工作时间允许,会发一些这样的文章,对前端一些比较重要的知识点深入理解。