乐趣区

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 对象}

接下来就是我想说的关键部分,也就是 resolvereject。平常写的代码可能都是下面这几种:

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 回调函数。

接下来可以自己尝试以下几种情况:

  1. 将对象 o 上的 resolve() 改成reject(),观察执行 then 方法的结果
  2. 在 o 的 then 里同时调用 reject()resolve(),并且调回先后位置。观察执行 then 后的结果。

总结一下,不论是 Promise.resolve() 还是构造函数中执行 resolve(),传入的如果既不是Promise 对象,也不是 thenable 对象,则返回的是一个完成状态的 Promise 对象。如果传入的是一个 Promise 对象,返回的是就是该对象。如果传入的是一个 thenable 对象,则返回的是作为 Promise 展开的 thenable 对象。

那说回开头提到的,resolve标记的之所以是一种完成状态,而不是成功,就在于如果传入的参数为 Promise 对象或者 thenable 对象时,如果这两者内部标记的状态为 reject,即便外面调用的是resolve,但执行的却是errorCallback。因此平时说Promise 的其中一种状态是成功的说法就是错误的。

退出移动版