接上一篇文章我们接着来说Promise
promise 全网最详解释,包括各方法和手动实现Promises/A+ 规范(1)
规范的第一部分,描述了几个术语的意思。
Promise 状态
promise 有 3 个状态,分别是 pending, fulfilled 和 rejected。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(){
this.state = PENDING;
this.result = null;
}
const transition = (promise,state,result) => {
if(promise.state !== PENDING) return
promise.state = state
promise.result = result
}
落实到代码上,大概像上面那样:
有 3 个常量 pending
, fulfilled
, rejected
,一个 Promise 构造函数,有 state
和 result
两个属性。当 state 为 fulfilled
时,result 作为 value
看待。当 state 为 rejected
时,result 作为 reason
看待。一个 transition
状态迁移函数,它只会在 state
为 pending
时,进行状态迁移。
Then 方法
promise 必须有 then 方法,接受 onFulfilled
和 onRejected
参数。
Promise.prototype.then = function(onFulfilled,onRejected){
}
onFulfilled
和 onRejected
如果是函数,必须最多执行一次。
onFulfilled
的参数是 value
,onRejected
函数的参数是 reason
。
then 方法可以被调用很多次,每次注册一组 onFulfilled 和 onRejected 的 callback。它们如果被调用,必须按照注册顺序调用。
我们为上面得 Promise 新增一个 callbacks 数组记录。
function Promise(){
this.state = PENDING;
this.result = null;
this.callbacks = [];
}
then 方法必须返回 promise。那 then 实现丰富化成下面这样:
Promise.prototype.then = function(onFulfilled,onRejected){
return new Promise((resolve,reject) =>{
let callback = {onFulfilled,onRejected,resolve,reject}
if(this.state == PENDING){
this.callbacks.push(callback)
} else {
setTimeout(()=>handleCallback(callback ,this.state,this.result),0)
}
})
}
在 then 方法里,return new Promise(f),满足 then 必须 return promise 的要求。
当 state 处于 pending 状态,就储存进 callbacks 列表里。
当 state 不是 pending 状态,就扔给 handleCallback 去处理。
handleCallback 是什么。不重要,我们只需要知道,它一定存在。我们得做一些处理,不是写死在 then 函数里,就是在外部的辅助函数里。为啥要套个 setTimeout?
因为我们不是在 JS 引擎层面实现 Promises,而是使用 JS 去实现 JS Promises。在JS里无法主动控制自身 execution context stack。可以通过 setTimeout/nextTick 等 API 间接实现,此处选用了 setTimeout。
then 方法返回的 promise,也有自己的 state 和 result。它们将由 onFulfilled 和 onRejected 的行为指定。这正是 handleCallback 要做的部分。
const handleCallback = (callback,state,result) => {
let {onFulfilled,onRejected,resolve,reject} = callback
try {
if(state === FULFILLED){
isFunction(onFulfilled) ? resolve(onFulfilled(result)):resolve(result)
} else if(state === REJECTED) {
isFunction(onRejected)?resolve(onRejected(result)):reject(result)
}
} catch (error) {
reject(error)
}
}
handleCallback 函数,根据 state
状态,判断是走 fulfilled 路径,还是 rejected 路径。
then 方法核心用途是,构造下一个 promise 的 result。
const resolvePromise = (promise,result,resolve,reject) =>{
if(result === promise){
let reason = new TypeError('can not fufill promise with itself')
return reject(reason)
}
if(isPromise(result)){
return result.then(resolve,reject)
}
if(isThenable(result)){
try{
let then = result.then
if(isFunction(then)){
return new Promise(then.bind(result)).then(resolve,reject)
}
} catch (error) {
return reject(error)
}
}
resolve(result)
}
至此,所有重要部分,都已经被处理。
接下来,我们只需要整合一下,把各部分衔接起来即可。其中,Promise 构造函数,扩充如下:
function Promise(f){
this.state = PENDING;
this.result = null;
this.callbacks = [];
let onFulfilled = value => transition(this,FULFILLED,value)
let onRejected = reason => transition(this,REJECTED,reason)
let ignore = false
let resolve = value => {
if(ignore) return
ignore = true
resolvePromise(this,value,onFulfilled,onRejected)
}
let reject = reason => {
if(ignore) return
ignore = true
onRejected(reason)
}
try{
f(resolve,reject)
} catch (error) {
reject(error)
}
}
构造 onFulfilled 去切换到 fulfilled 状态,构造 onRejected 去切换到 rejected 状态。
构造 resolve 和 reject 函数,在 resolve 函数里,通过 resolvePromise 对 value 进行验证。
配合 ignore 这个 flag,保证 resolve/reject 只有一次调用作用。
最后将 resolve/reject 作为参数,传入 f 函数。
若 f 函数执行报错,该错误就作为 reject 的 reason 来用。
const handleCallbacks = (callbacks,state,result) =>{
while(callbacks.length) handleCallback(callbacks.shift(),state,result)
}
const transition = (promise,state,result) =>{
if(promise.state !== PENDING) return
promise.state = state
promise.result = result
setTimeout(() => handleCallbacks(promise.callbacks,state,result),0)
}
transition 函数扩充如上,当状态变更时,异步清空所有 callbacks。之前我们已经实现了 handleCallback,实现 handleCallbacks 只需要一个循环。
有了 then 方法,我们可以很容易实现 ES2015 Promises 的几个扩充方法。
Promise.prororype.catch = function(onRejected){
return this.then(null,onRejected)
}
Promise.resolve = value => new Promise(resolve => resolve(value))
Promise.reject= reason => new Promise((_,reject)=> reject(reason))
catch 方法和 resolve/reject 静态方法的实现如上所示。Promise.all 和 Promise.race 的实现,留给感兴趣的读者,作为练习。
全部 passing。Promises/A+ 规范实现完毕。关于 Promises 还有好多话题可以讨论,比如它与函数式的 Monad 的关系,比如 Promise/A+ 规范中值得商榷和可以改进的部分等。