优化手机网站池州有哪些做网站的

前言
  这可能是手写promise较清晰的文章之一。
  由浅至深逐步分析了原生测试用例,以及相关Promise/A+规范。阅读上推荐以疑问章节为切入重点,对比Promise/A+规范与ECMAScript规范的内在区别与联系,确定怎样构建异步任务和创建promise实例。然后开始手写章节,过程中代码与测试可参考 promise-coding 仓库。
也试着回答以下关键问题。
- 什么是广义对象?
 - 如何检验
promise类型? promise与thenable两类型有何区别?
疑问
  如果不太清楚Promise,建议参考《ECMAScript 6 入门》,预习下Promise相关用法知识。
除此之外,对规范也要有大致认识,我们将根据几个疑问来细致阐述。
什么是 Promise/A+ 规范?
  Promise有多种社区规范,例如 Promise/A、Promise/B、Promise/D 和 Promises/KISS 等。
  Promise/A+ 在Promise/A之上,规范了术语并拓展了参数行为,省略了一些有问题的部分。
  Promise/A+有很多实现,例如第三方库 q、when 和 bluebird 等。实际上任何Promise通过测试,都被认为是对Promise/A+规范的一种实现。
Promise/A+规范官方测试用例为 promises-aplus-tests
原生 Promise 实现了 Promise/A+?
  在Promise/A+规范 The ECMAScript Specification 章节中。
The ECMAScript Specification
...
Largely due to the actions of the Promises/A+ community, the Promise global specified by ECMAScript and present in any conforming JavaScript engine is indeed a Promises/A+ implementation!
  叙述了JavaScript引擎中的Promise也是对Promise/A+规范的一种实现。
为什么呢?
  Promise/A+规范内容上仅说明了Promise状态和then方法。
  ECMAScript规范不仅规定Promise为构造函数,还添加了静态方法,例如Promise.resolve、Promise.all和Promise.race等,新增了原型方法Promise.prototype.catch和Promise.prototype.finally等。其中Promise.prototype.then相关内容,则是根据Promise/A+规范转化而来。
  我们知道,JavaScript就是对ECMAScript规范的一种实现,而ECMAScript规范中Promise.prototype.then相关内容又继承了Promise/A+规范。
  那么可以说,JavaScript引擎中的Promise,即原生Promise,就是对Promise/A+规范的一种实现。
如何构建异步任务?
  Promise/A+规范规定then方法接收两个参数。
promise.then(onFulfilled, onRejected)
 
在 2.2.4 小结中明确了参数必须以异步形式运行。
2.2.4. onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
  注解 3.1 补充可用宏任务setTimeout和setImmediate,或者微任务MutationObserver(浏览器环境)和process.nextTick(node环境)达到异步。
3.1. ...In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a "macro-task" mechanism such as setTimeout or setImmediate, or with a "micro-task" mechanism such as MutationObserver or process.nextTick.
  综上所述,Promise/A+规范仅规定了参数以异步形式运行,并未规定是宏任务还是微任务。
注意
V8引擎内部为微任务,考虑一致性推荐 queueMicrotask 创建微任务,兼容性相对较好
如何创建 promise?
  Promise/A+规范并未提及如何创建promise。
  ECMAScript6规范发布以来,多数是以构造函数方式创建promise。
new Promise(executor)
 
  实际上在此之前,还流行一种Deferred方式,例如 JQuery.Deferred。
$.Deferred()
 
我们以定时器为例,对比下两者差异。
// ECMAScript6 Promise
const promise = new Promise(resolve => {setTimeout(() => {resolve(1)}, 1000)
})promise.then(v => {console.log(v) // 1
})// JQuery Deferred
const deferred = $.Deferred()deferred.promise().then(v => {console.log(v) // 1
})setTimeout(() => {deferred.resolve(1)
}, 1000)
 
  你也注意到了吧,Deferred方式相对更加灵活,可以在任何时机修改状态。而Promise方式自由度减少了很多,不仅代码层级多了一层,而且只能在函数参数中修改状态。
  可能你会问,那为什么TC39放弃了Deferred,而决定了Promise构造器方式呢?
  Deferred方式存在一个较严重的缺陷,即在业务流程阶段,不容易捕获异常。
const deferred = $.Deferred()deferred.promise().catch(v => {console.log(v)
});(function () {throw new Error() // Uncaught Errordeferred.resolve(1)
})()
 
  如果想让promise捕获异常,业务代码可修改为。
;(function () {try {throw new Error()} catch (error) {deferred.reject(error)}deferred.resolve(1)
})()
 
  而Promise构造器方式则非常容易。
const promise = new Promise(resolve => {throw new Error()resolve(1)
})promise.catch(v => {console.log(v) // Error
})
 
  两相比较下ECMAScript6确定了以构造器方式创建promise。
个人认为
Deferred更多是一个发布订阅器,而Promise则相对更加强大,是一个异步流程解决方案,ECMAScript6规范将其独立为一个模块是相当合理的
手写
  Promise/A+更多地是规范了算法逻辑,并未规定语法层面的实现方式。
  我们可以参考原生Promise语法,并结合简单用例,手写以符合Promise/A+规范。
注意
Promise/A+规范相关内容将特别标注
实例初始属性
  原生创建Promise实例。
new Promise(() => {})
// {
//   [[PromiseState]]: 'pending',
//   [[PromiseResult]]: undefined,
// }
 
相关特征包括。
Promise为构造函数- 默认状态为
pending,默认结果为undefined - 三种状态分别为等待态
pending、解决态fulfilled和拒绝态rejected——「Promise/A+ 2.1」 
  代码编写如下,其中属性[[PromiseState]]用于保存状态,[[PromiseResult]]用于保存结果。
const PromiseState = '[[PromiseState]]'
const PromiseResult = '[[PromiseResult]]'const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'class Promise {[PromiseState] = PENDING;[PromiseResult] = undefined
}
 
ES2020规范 proposal-class-fields 允许实例属性定义在类内部的最顶层,相对更加清晰简洁
executor 执行器
  原生Promise传参函数。
new Promise(function executor() {console.log(1) // 1
})
console.log(2) // 2new Promise((resolve, reject) => {resolve(3)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 3,
// }new Promise((resolve, reject) => {reject(4)
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 4,
// }
 
相关特征包括。
- 实例创建过程中参数
executor将同步执行 - 执行器
executor包括resolve和reject两个函数参数,resolve执行实例状态修改为解决态,reject执行实例状态修改为拒绝态 
  以下为优化代码,注意私有方法用箭头函数,可将内部this指向实例对象。
class Promise {...#resolve = value => {this[PromiseState] = FULFILLEDthis[PromiseResult] = value}#reject = reason => {this[PromiseState] = REJECTEDthis[PromiseResult] = reason}constructor(executor) {executor(this.#resolve, this.#reject)}
}
 
ES2020规范 proposal-class-fields 允许实例定义私有属性或方法,仅可在类内部使用,外部无法使用
状态不可变性
  原生Promise修改状态。
new Promise((resolve, reject) => {resolve(1)resolve(2)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }new Promise((resolve, reject) => {resolve(3)reject(4)
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 3,
// }
 
相关特征包括。
- 处于解决态或者拒绝态,一定不能再修改为任何状态——「Promise/A+ 2.1.2 / 2.1.3」
 - 处于等待态的时候,可能变为解决态或者拒绝态——「Promise/A+ 2.1.1」
 
代码优化为。
#resolve = value => {if (this[PromiseState] === PENDING) {this[PromiseState] = FULFILLEDthis[PromiseResult] = value}
}
#reject = reason => {if (this[PromiseState] === PENDING) {this[PromiseState] = REJECTEDthis[PromiseResult] = reason}
}
 
方法传参
  原生Promise上then方法传参。
const p = new Promise((resolve, reject) => {resolve()
})p.then(undefined, undefined)
 
相关特征包括。
promise必须有then方法,且接收两个参数onFulfilled和onRejected——「Promise/A+ 2.2」onFulfilled和onRejected都是可选参数,若不是函数,必须被忽略——「Promise/A+ 2.2.1」onFulfilled和onRejected一定被作为普通函数调用——「Promise/A+ 2.2.5」
代码修改为。
class Promise {...then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {}onRejected = typeof onRejected === 'function' ? onRejected : () => {}}
}
 
参数为非函数时,为保证可被调用,暂时返回普通函数
then 方法
  原生Promise执行then方法。
const p1 = new Promise((resolve, reject) => {resolve(1)
})
p1.then(v => {console.log(v) // 1
})const p2 = new Promise((resolve, reject) => {reject(2)
})
p2.then(undefined, v => {console.log(v) // 2
})
 
相关特征包括。
- 如果
onFulfilled或onRejected是一个函数,必须在promise被解决或被拒绝后调用,且promise值或原因作为第一个参数——「Promise/A+ 2.2.2 / 2.2.3」 
代码修改为。
then(onFulfilled, onRejected) {...if (this[PromiseState] === FULFILLED) {onFulfilled(this[PromiseResult])}if (this[PromiseState] === REJECTED) {onRejected(this[PromiseResult])}
}
 
异步 executor
  目前代码并未完全符合「Promise/A+ 2.2.2 / 2.2.3」规范,例如executor为异步情况时,还会存在一些问题。
const p = new Promise((resolve, reject) => {setTimeout(() => {resolve(1)}, 1000)
})p.then(v => {console.log(v)
})
 
控制台没有打印任何内容
为什么呢?
  实例p在创建完成后,还处在等待态。紧接着执行then方法,then方法内部没有等待态相关逻辑,也就没有任何操作。1s后resolve执行,也仅仅是将p状态修改为解决态。
如何解决呢?
  可以在等待态就保存onFulfilled和onRejected函数,在resolve修改状态时再执行。
代码优化为。
class Promise {...#onFulfilledCallBack = undefined#onRejectedCallBack = undefined#resolve = value => {if (this[PromiseState] === PENDING) {...this.#onFulfilledCallBack?.(this[PromiseResult])}}#reject = reason => {if (this[PromiseState] === PENDING) {...this.#onRejectedCallBack?.(this[PromiseResult])}}...then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBack = onFulfilledthis.#onRejectedCallBack = onRejected}}
}
 
?.为ES2020规范中 proposal-optional-chaining 可选链操作符
多次调用 then
  原生Promise多次调用then方法。
const p = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})p.then(() => {console.log(1) // 1
})p.then(() => {console.log(2) // 2
})p.then(() => {console.log(3) // 3
})
 
相关特征包括。
then方法函数参数按语法顺序执行- 同一
promise上then方法可能被多次调用——「Promise/A+ 2.2.6」 
代码优化如下,注意为了保证顺序,两数组内函数都是先进先出。
class Promise {...#onFulfilledCallBacks = []#onRejectedCallBacks = []#resolve = value => {if (this[PromiseState] === PENDING) {...while (this.#onFulfilledCallBacks.length) {this.#onFulfilledCallBacks.shift()(this[PromiseResult])}}}#reject = reason => {if (this[PromiseState] === PENDING) {...while (this.#onRejectedCallBacks.length) {this.#onRejectedCallBacks.shift()(this[PromiseResult])}}}...then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(onFulfilled)this.#onRejectedCallBacks.push(onRejected)}}
}
 
返回 promise
  原生Promise返回值。
const p = new Promise(() => {})p.then()
// {
//   [[PromiseState]]: 'pending',
//   [[PromiseResult]]: undefined,
// }
 
相关特征包括。
then方法必须返回一个新promise——「Promise/A+ 2.2.7」
代码暂时修改为。
then(onFulfilled, onRejected) {...if (this[PromiseState] === PENDING) {...}const promise = new Promise(() => {})return promise
}
 
函数参数返回值
  原生Promise函数参数onFulfilled返回数值。
const p = new Promise((resolve, reject) => {resolve()
})p.then(() => {return 1
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }
 
相关特征包括。
- 如果
onFulfilled或onRejected返回一个值x,运行promise解决程序——「Promise/A+ 2.2.7.1」 - 如果
x不是一个对象或函数,用x解决promise——「Promise/A+ 2.3.4」 
  何为promise解决程序呢?
  「Promise/A+ 2.3」叙述是一个抽象操作,可表示为[[Resolve]](promise, x)。其中主要根据x类型,决定新promise的状态和结果。
  比如x不是一个对象或函数,例如数值,则新promise状态将确定为解决态,结果为x,即用x解决promise。
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: x,
// }
 
  那么如何在onFulfilled或onRejected返回数值x时,又修改新promise状态和结果呢?
then(onFulfilled, onRejected) {...if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])}...const promise = new Promise(() => {})return promise
}
 
你可能想到了。
then(onFulfilled, onRejected) {...const promise = new Promise(() => {})if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])promise.#resolve(x)}...return promise
}
 
可修改状态也符合规范,但个人认为此方式存在一些缺陷。
  将实例属性resolve私有化,就是为了限制外部访问。以promise.#resolve访问,而非this.#resolve,已经处于外部访问的范畴了,思路上不是很合理。
还有更好的办法吗?
  我们知道在executor执行器上,resolve和reject两个参数也可修改状态。
  如果将if语句体迁移至executor内部,有没有可能呢?答案是可以的。
then(onFulfilled, onRejected) {...const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolve(x)}...})return promise
}
 
if语句体在executor外部时,同步执行。在executor内部时,也是同步执行
相关特征完全实现了吗?并没有。
  若executor为异步情况时,还存在一些问题。
const p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})const p2 = p1.then(() => {return 2
})setTimeout(() => {console.log(p2)// {//   [[PromiseState]]: 'pending',//   [[PromiseResult]]: undefined,// }
}, 2000)
 
控制台打印内容与原生不一致
为什么呢?
  实例p1处于等待态,执行then方法将onFulfilled保存至数组中。1s后resolve执行,p1状态修改为解决态,紧接着取出运行onFulfilled,p2状态无任何变化。
  我们可以在onFulfilled执行时,对返回值x稍加处理。
const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolve(x)})...}
})
 
处理函数
  为了统一处理不同类型x值,并严格实现规范「Promise/A+ 2.3」中各子章节。
  修改代码并创建resolvePromise函数,参数暂时为x和resolve。
class Promise {...then(onFulfilled, onRejected) {...const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolvePromise(x, resolve)}...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(x, resolve)})...}})return promise}
}function resolvePromise(x, resolve) {resolve(x)
}
 
研读部分子章节。
2.3.1. If promise and x refer to the same object, reject promise with a TypeError as the reason.
2.3.2.2. If/when x is fulfilled, fulfill promise with the same value.
2.3.2.3. If/when x is rejected, reject promise with the same reason.
  可确认参数promise和x、resolve、reject。
const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)}...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)})...}
})function resolvePromise(promise, x, resolve, reject) {resolve(x)
}
 
抛出异常
  原生Promise抛出异常。
const p = new Promise((resolve, reject) => {resolve()
})p.then(() => {throw new Error()
}).then(undefined, v => {console.log(v) // Error
})
 
相关特征包括。
- 如果
onFulfilled或onRejected抛出一个异常e,新promise为拒绝态且原因为e——「Promise/A+ 2.2.7.2」 
代码优化为。
const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}}...
})
 
  类似地executor为异步情况时,也存在一些问题。
const p = new Promise((resolve, reject) => {setTimeout(() => {resolve()}, 1000)
})p.then(() => {throw new Error() // Uncaught Error
}).then(undefined, v => {console.log(v)
})
 
未捕获到异常
为什么呢?
  实例p处于等待态,执行then方法将onFulfilled保存。1s后resolve执行,p状态修改为解决态,紧接着取出onFulfilled,运行内部抛出了异常。
代码优化为。
const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})...}
})
 
异步任务
  原生Promise异步。
const p = new Promise((resolve, reject) => {resolve(1)
})console.log(2) // 2
p.then(v => {console.log(v) // 1
})
console.log(3) // 3
 
注意打印顺序
2 3 1
相关特征包括。
onFulfilled或onRejected必须以异步形式运行——「Promise/A+ 2.2.4」
代码简单修改为。
const queueTask = queueMicrotaskclass Promise {...then() {const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {try {queueTask(() => {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)})} catch (e) {reject(e)}}...})return promise}
}
 
  注意try...catch并不能捕获到异步函数内抛出的异常,例如。
try {setTimeout(() => {throw new Error() // Uncaught Error})
} catch (error) {console.log(error)
}
 
那如何优化呢?
  我们可以将全部try...catch语句放到异步函数中。
const promise = new Promise((resolve, reject) => {if (this[PromiseState] === FULFILLED) {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}...
})
 
  类似地executor为异步情况时,也存在一些问题。
const p = new Promise(resolve => {setTimeout(() => {console.log(1) // 1resolve(2)console.log(3) // 3}, 1000)
})p.then(v => {console.log(v) // 2
})
 
打印顺序
1 2 3(原生打印顺序1 3 2)
为什么呢?
  onFulfilled没有以异步形式运行。
代码修改为。
const promise = new Promise((resolve, reject) => {...if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(() => {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})})...}
})
 
合并重复代码。
const promise = new Promise((resolve, reject) => {const resolved = () => {queueTask(() => {try {const x = onFulfilled(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}const rejected = () => {queueTask(() => {try {const x = onRejected(this[PromiseResult])resolvePromise(promise, x, resolve, reject)} catch (e) {reject(e)}})}if (this[PromiseState] === FULFILLED) {resolved()}if (this[PromiseState] === REJECTED) {rejected()}if (this[PromiseState] === PENDING) {this.#onFulfilledCallBacks.push(resolved)this.#onRejectedCallBacks.push(rejected)}
})
 
参数优化
  原生Promise值穿透。
const p1 = new Promise((resolve, reject) => {resolve(1)
})
p1.then(undefined)
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }const p2 = new Promise((resolve, reject) => {reject(2)
})
p2.then(undefined, undefined)
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 2,
// }
 
相关特征包括。
- 如果
onFulfilled不是一个函数且原promise被解决,新promise必须也被解决,且值与原promise相同——「Promise/A+ 2.2.7.3」 - 如果
onRejected不是一个函数且原promise被拒绝,新promise必须也被拒绝,且原因与原promise相同——「Promise/A+ 2.2.7.4」 
代码优化如下。
then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => valueonRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }...
}
 
注意
throw抛出异常将被try...catch捕获,进而拒绝新promise
类型
  如何处理不同类型x呢?
  还是参考规范「Promise/A+ 2.3」各子章节,以优化resolvePromise处理函数。
循环引用
  原生Promise循环引用。
const p1 = new Promise((resolve, reject) => {resolve()
})const p2 = p1.then(() => {return p2
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: TypeError: Chaining cycle detected for promise #<Promise>,
// }
 
相关特征包括。
- 如果
promise和x引用同一对象,则拒绝promise,原因为一个TypeError——「Promise/A+ 2.3.1」 
代码优化为。
function resolvePromise(promise, x, resolve, reject) {if (promise === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}resolve(x)
}
 
传递性
  原生Promise函数参数onFulfilled返回promise。
const p1 = new Promise((resolve, reject) => {resolve()
})
const p2 = new Promise((resolve, reject) => {reject(1)
})p1.then(() => {return p2
})
// {
//   [[PromiseState]]: 'rejected',
//   [[PromiseResult]]: 1,
// }
 
相关特征包括。
- 如果
x是等待态,promise必须保持等待态,直到x被解决或被拒绝——「Promise/A+ 2.3.2.1」 - 如果
x是解决态,用相同的值解决promise——「Promise/A+ 2.3.2.2」 - 如果
x是拒绝态,用相同的原因拒绝promise——「Promise/A+ 2.3.2.3」 
  也就是promise状态与x始终都保持一致。
  可能会存在x初始为等待态,然后又转变为解决态或拒绝态。过程中两者状态始终一致,若x状态转变,promise状态也将转变。
  那如何知道x状态转变呢?答案就是then方法。
x.then(onFulfilled, onRejected)
 
  x转变为解决态时将运行onFulfilled,转变为拒绝态时将运行onRejected。
  那我们就可在onFulfilled或onRejected内部去修改promise状态。
代码优化为。
function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {x.then(value => {resolve(value)}, reason => {reject(reason)})} else {resolve(x)}
}
 
简化为。
function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {x.then(resolve, reject)} else {resolve(x)}
}
 
广义对象
何为广义对象呢?
能添加属性或方法的变量,都称之为广义上的对象,例如数组、函数等。
  创建isObject工具函数,更多参考 lodash.isObject。
function isObject(value) {const type = typeof valuereturn value !== null && (type === 'object' || type === 'function')
}
 
然后阅读规范「Promise/A+ 2.3.3」小节,省略部分暂时不考虑。
- 如果
x是一个对象或函数(广义对象)- 让
then为x.then - 如果获取
x.then导致抛出了一个异常e,用e作为原因拒绝promise - 如果
then是一个函数,用x作为this调用它,且包含两个参数,分别为resolvePromise和rejectPromise- 如果
resolvePromise用一个值y调用,运行[[Resolve]](promise, y) - 如果
rejectPromise用一个原因r调用,用r拒绝promise ...- 如果调用
then抛出了一个异常e...- 否则用
e作为作为原因拒绝promise 
 
 - 如果
 - 如果
then不是一个函数,用x解决promise 
 - 让
 
转译为代码。
function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {...} else {if (isObject(x)) {var thentry {then = x.then} catch (e) {reject(e)}if (typeof then === 'function') {try {then.call(x,y => {resolvePromise(promise, y, resolve, reject)},r => {reject(r)})} catch (e) {reject(e)}} else {resolve(x)}} else {resolve(x)}}
}
 
  规范中运行[[Resolve]](promise, y),即递归resolvePromise,为什么呢?
  原因在于y值可能还是promise或者广义对象等等。
  我们来看一个原生Promise示例。
const p = new Promise(resolve => {resolve()
})
const thenable1 = {then(reslove) {reslove(1)},
}
const thenable2 = {then(resolve) {resolve(thenable1)},
}p.then(() => {return thenable2
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 1,
// }
 
优先级
以下为刚才省略的部分。
- 如果
then是一个函数.........- 如果
resolvePromise和rejectPromise都被调用,或者对其中一个多次调用,那么第一次调用优先,以后的调用都会被忽略 - 如果调用
then抛出了...- 如果
resolvePromise或rejectPromise已经被调用,则忽略它 ...
 - 如果
 
 
为了限制哪种情况呢?
  还是来看一个原生Promise示例。
const p = new Promise(resolve => {resolve()
})
const thenable1 = {then(reslove) {setTimeout(() => {reslove(2)}, 0)},
}
const thenable2 = {then(resolve) {resolve(thenable1)resolve(1)},
}p.then(() => {return thenable2
})
// {
//   [[PromiseState]]: 'fulfilled',
//   [[PromiseResult]]: 2,
// }
 
代码如何优化呢?
  我们可定义布尔变量called,标记是否运行参数resolvePromise或rejectPromise。然后在第一次运行时将called修改为true,而以后的都会return被忽略。
if (typeof then === 'function') {var called = falsetry {then.call(x,y => {if (called) returncalled = trueresolvePromise(promise, y, resolve, reject)},r => {if (called) returncalled = truereject(r)})} catch (e) {if (called) returncalled = truereject(e)}
}
 
thenable
规范「Promise/A+ 1.1」小结陈述了。
  promise是一个对象或函数(广义对象),存在then方法且行为符合规范。
  第三方Promise库、原生Promise以及我们手写版本Promise,创建的promise实例,其实都是标准的promise类型。
  而代码中x instanceof Promise语句,检验是否为promise类型,就有问题了。例如x被第三方库创建,也是标准promise类型,但是并不会运行if语句体,而是错误地运行else语句体。
function resolvePromise(promise, x, resolve, reject) {...if (x instanceof Promise) {...} else {...}
}
 
  还有方法可确定x为promise类型吗?答案是没有。
怎么办呢?
  既然无法检验promise类型,那就退而求其次,检验类似promise类型的,即鸭式辩型。
鸭子类型(
duck typing),也叫鸭式辩型,一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子
  规范「Promise/A+ 1.2」提出了thenable类型,即定义了then方法的对象或函数。
{then() {...},
}
 
thenable是promise的鸭子类型
  检验是否为promise类型,则降级为检验是否为thenable类型。
代码修改为。
function resolvePromise(promise, x, resolve, reject) {if (promise === x) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}if (isObject(x)) {var thentry {then = x.then} catch (e) {reject(e)}if (typeof then === 'function') {var called = falsetry {then.call(x,y => {if (called) returncalled = trueresolvePromise(promise, y, resolve, reject)},r => {if (called) returncalled = truereject(r)})} catch (e) {if (called) returncalled = truereject(e)}} else {resolve(x)}} else {resolve(x)}
}
 
测试
安装官方测试用例 promises-aplus-tests。
npm i promises-aplus-tests -D
 
  promise代码中新增以下。
// promise.js
class Promise {...
}Promise.deferred = () => {const result = {}result.promise = new Promise((resolve, reject) => {result.resolve = resolveresult.reject = reject})return result
}module.exports = Promise
 
新增测试命令。
// package.json
{..."scripts": {"test": "promises-aplus-tests promise.js"},..."devDependencies": {"promises-aplus-tests": "^2.1.2"}
}
 
  运行npm run test。

小结
  全文共计两万五千字有余,参考Promise/A+规范手写了then方法和promise解决程序。
  相关代码可参考 promise-coding 仓库,支持node和浏览器环境测试。
  如何手写Promise到此就结束了。
扩展
学有余力或意犹未尽的伙伴们。
  贴出两个代码片段,可在原生Promise与手写Promise环境下运行。
// 1
new Promise(resolve => {resolve(Promise.resolve())
}).then(() => {console.log(3)
})Promise.resolve().then(() => {console.log(1)}).then(() => {console.log(2)}).then(() => {console.log(4)})// 2
Promise.resolve().then(() => {console.log(0)return Promise.resolve()}).then(() => {console.log(4)})Promise.resolve().then(() => {console.log(1)}).then(() => {console.log(2)}).then(() => {console.log(3)}).then(() => {console.log(5)}).then(() => {console.log(6)})
 
看看能否分析出两者之间的细微差异?答案是不能。
更多请持续关注更文,或在参考链接中探索一二。
参考
- Promise/A+ 规范译文
 - 原生 Promise 和手写 Promise 的区别是什么?
 - resolve 时序
 - V8 源码解读 Promise
 
🎉 写在最后
🍻伙伴们,如果你已经看到了这里,觉得这篇文章有帮助到你的话不妨点赞👍或 Star ✨支持一下哦!
手动码字,如有错误,欢迎在评论区指正💬~
你的支持就是我更新的最大动力💪~
GitHub / Gitee、GitHub Pages、掘金、CSDN 同步更新,欢迎关注😉~
