Promise知识的一些边角料
Promise现在已经成为日常开发绕不过去的一个API了,并且也是面试中最喜欢被问到的部分,所以相信大家对它都有一个最基本的认识。所以我并不会再详细介绍这个API的方方面面,而是说一些可能大家日常没有注意到的地方。
Promise作为处理异步函数更好的解决方案,优点在于他有一个初始状态,并且状态发生变化之后就不会再改变,也就是pendding(等待)
,resolve(完成)
和reject(拒绝)
。
当执行new Promise(...)
之后,返回的是一个Promise
对象,虽然这个代码写了不知道多少遍,但是我并没有想过一个问题:Promise
对象它有什么特点?怎么才能算一个Promise
对象?
查了一些资料之后了解到,Promise
对象是具有thenable特征的对象,也就是这个对象上具有then
这个属性,不论这个属性是属于对象自身还是存在于原型链的某一处。所以总结一下就是,thenable
对象不一定是Promise
对象,但是Promise
对象一定具有thenable
特征。
可以看下面这段代码:
let p = new Promise((resolve,reject)=>{}) if(p !== null && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function'){ // thenable 对象 }else{ // 非 thenable 对象 }
接下来就是我想说的关键部分,也就是resolve
和reject
。平常写的代码可能都是下面这几种:
let something let p = new Promise((resolve,reject)=>{ // 第一种 resolve(something) // 第二种 reject(something) }).then(resolveCallback,errorCallback) // 第三种 Promise.resolve(something).then(resolveCallback) // 第四种 Promise.reject(something).then(errorCallback)
Promise
本身代表着一种承诺,而且是指向未来的,所以他就有可能成功有可能失败。reject
明确表示的是失败状态,然而resolve
的翻译是处理完成,这里隐含的意思是并不是明确表示这个承诺就一定成功。
所以对于上面的代码,当something
是一个常量(比如数字),根据执行的方法,resolveCallback
或者errorCallback
就会被执行。了解过少许Promise
的原理就会知道,执行Promise.resolve()
,js会将传入的参数转换为Promise
对象返回,那如果传去的不是一个常量而是一个新的Promise
对象又会如何?
let p1 = Promise.resolve('1') let p2 = Promise.resolve(p1) p2.then(res=>{ console.log(res) // 这里返回的是什么呢 })
大家可以试着运行一下,结果是1。如果想不明白原因,可以看下面这部分:
let p1 = Promise.resolve('1') let p2 = Promise.resolve(p1) console.log(p1 === p2)
此时的结果是true
,也就是说把一个Promise
对象作为参数传给Promise.resolve
,返回的是依旧是传入的Promise
对象。以下的结果也是一样:
let p1 = new Promise((resolve, reject) => { resolve(1) }) let p2 = Promise.resolve(p1) console.log(p1 === p2) // true
但是如果把上面代码中的resolve
改成reject
,则判断条件就是false
。也就是说reject
并不具备这个特性。
前面说过,Promise
对象是具有thenable
特性的对象,那现在传入Promise.resolve
里的如果就是一个具备then
属性的对象,又会如何呢?
let o = { then(resolve,reject){ } } Promise.resolve(o)
这里什么都不会发生,此时如果打印Promise.resolve(o)
,会发现控制台显示这是一个处于pendding
状态的Promise
。
用过Promise
的知道,它具备一个then
方法,有两个参数,第一个是表示resolve
的回调函数,第二个是表示reject
的回调函数。所以尝试改一下上面的代码。
let o = { then(resolve,reject){ resolve(1) } } Promise.resolve(o).then(res=>{ console.log(res) // 1 })
当执行对象o上的then属性里的第一个回调函数,作用就类似于调用构造函数是调用的resolve
回调函数。
接下来可以自己尝试以下几种情况:
- 将对象o上的
resolve()
改成reject()
,观察执行then方法的结果 - 在o的then里同时调用
reject()
和resolve()
,并且调回先后位置。观察执行then后的结果。
总结一下,不论是Promise.resolve()
还是构造函数中执行resolve()
,传入的如果既不是Promise
对象,也不是thenable
对象,则返回的是一个完成状态的Promise
对象。如果传入的是一个Promise
对象,返回的是就是该对象。如果传入的是一个thenable
对象,则返回的是作为Promise
展开的thenable
对象。
那说回开头提到的,resolve
标记的之所以是一种完成状态,而不是成功,就在于如果传入的参数为Promise
对象或者thenable
对象时,如果这两者内部标记的状态为reject
,即便外面调用的是resolve
,但执行的却是errorCallback
。因此平时说Promise
的其中一种状态是成功的说法就是错误的。