Promise()构造函数
用于构造一个 promise对象。
executor - 执行器函数
Promise()接收一个双参函数,参数为resolve和reject。executor会被立即执行。
当resolve和reject被调用时,promise对象发生状态变化并执行相对应状态的回调。
除此之外,当executor执行的时候发生错误,会立即返回rejected状态的promise。
参数
- resolve()
- reject()
resolve和reject最多只能接受一个参数,传递多个参数时,除第一个后都会被忽略。
const executor = (resolve,reject) => {
const random = Math.random()
random < 0.5 ? resolve(random) : reject(random)
}
const p = new Promise(executor)
promise对象
一个 promise 对象保存了它当前的状态以及返回值。当 promise 状态发生变更时去执行相对应的回调。
状态
- pending
- fulfilled
- rejected
一个promise通过状态的变更来确定它将执行哪一个回调。
下面有一张mdn的图更好的描述了promise的行为
then()
接收两个函数作为参数,可选值,用来注册fulfilled状态和rejected状态的回调。
const onFulfillment = (data) => {
console.log(data)
}
const onRejection = (data) => {
console.log('onRejection',data)
}
p.then(onFulfillment, onRejection)
catch()
catch也可以用来注册rejected状态的回调。
p.then().catch(onRejection)
// 等价
p.then(,onRejection)
finally()
finally注册的回调无论promise处于fulfilled还是rejected都一定会执行。
const onFinally = (data) => {
console.log('onFinally',data)
}
p.finally(onFinally)
// 等价
p.then(onFinally,onFinally)
返回值
promise对象上的api返回值都是promise。
Promise/A+ 标准
An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
Promise/A+ 标准提供了统一的promise行为。根据标准,则可以实现互相操作的Promise。 原文将放在参考链接里。
统一术语
- promise 是一个拥有符合该标准then方法的对象或函数。
- thenable 是一个拥有then方法的对象或函数。
- value 是promise状态成功时的值,包括 undefined/thenable或者是 promise。
- exception 是一个使用throw抛出的异常值。
- reason 是 promise 处于失败状态时的值。
promise 的状态
- pending
- fulfilled
- rejected
promise 必须处于以上三个状态之一。
处于pending时
- promise 的状态可以变更为 fulfilled 或者 rejected。
处于fulfilled时
- 无法再转变状态。
- 必须有一个 value。
处于rejected时
- 无法再转变状态。
- 必须有一个 reason。
then()
promise必须提供一个then方法来访问最终的value或reason。
接收两个可选参数 onFulfilled 和 onRejected。
参数都必须是函数类型。
onFulfilled存在时
- promise 变为 fulfilled 时, 调用 onFulfilled, 参数为 value 。
- promise的状态不是fulfilled之前不能调用。
- 只能被调用一次。
onFulfilled存在时
- promise 变为 rejected 时, 调用 onFulfilled, 参数为 reason 。
- promise的状态不是 rejected 之前不能调用。
- 只能被调用一次。
onFulfilled 和 onRejected 应该是 异步执行。即应该是 宏任务或者微任务。宏任务可通过 setTimeout 或者 setImmediate(node) ,微任务可通过 MutationObserver 或 process.nextTick(node)。
onFulfilled 和 onRejected 应该被作为函数调用,并且没有this。(严格模式为undefined, 宽松模式应该为全局对象)。
then可以在同一promise上被多次调用
- promise变更为 fulfilled状态时,所有的onFulfilled回调都需要按照then的顺序执行
- promise变更为 rejected状态时,所有的onRejected回调都需要按照then的顺序执行
then必须返回一个promise
promise2 = promise1.then(onFulfilled, onRejected);
- onFulfilled 或 onRejected 返回一个值 x。则运行 Promise Resolution Procedure。
- onFulfilled 或 onRejected 抛出一个异常 e, promise2 必须用 e 作为 reason 被 reject 。
- onFulfilled 不是一个函数并且 promise1 被 resolve 时, promise2必须以同样的值被 resolve 。
- onRejected 不是一个函数并且 promise1 被 reject 时, promise2 必须以同样的值被 reject 。
Promise Resolution Procedure
Promise Resolution Procedure 其实就是一个决定 promise2 和 x 最终行为的函数 ,记为 [[Resolve]](promise, x)。
- 如果promise 和 x 引用同一个对象,用一个TypeError 作为 reason 来拒绝promise。
- 如果x是一个promise,采用它的状态。
- 否则如果 x是一个对象或函数。
- 让then成为x.then
- 如果检索x.then抛出异常 e ,用 e 作为 reason 拒绝 promise。
- 如果then是一个函数, 用x 作为this调用它。 then 方法的参数为两个回调函数。 第一个是 resovlePromise,第二个是rejectPromise:
- 如果resovlePromise用一个 value - y调用,再次允许 [[Resolve]](promise, y)。
- 如果 rejectPromise 用一个 reason - r 调用,用r 拒绝promise。
- 如果 resovlePromise 或 rejectPromise 都被调用, 或者对用一个参数进行多次调用,那么第一个调用优先。以后调用都忽略。
- 如果调用 then 抛出异常。
- 如果 resovlePromise 或 rejectPromise 都被调用, 忽略这个异常。
- 否则 用 e 作为 reason 拒绝 promise。
- 如果 then 不是一个函数 用 x 解决 promise
- 如果 x 不是一个对象或函数, 用 x 解决 promise。
手动实现
通过规范 可以发现 主要分为3个方面。
- 状态。规定了状态的值和状态变化的行为和意义。
- then函数。定义了then函数的行为,是promise的交互接口。
- promise解决程序。 用来决策then函数返回的新promise和then函数接收到的回调被执行后的返回值的行为。
通过查看内置promise对象,可以发现promise并未暴露太多属性。但是很多文章都是通过直接在对象挂载属性,并且未做太多严格的限制,实际可以直接修改自定义实现的很多属性。这里的实现尽量更严格一些。
- 状态只能是pending,fulfilled,rejected之一
- 变为 fulfilled, rejected 后无法再改变
// 状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 为了不暴露promise的其他信息 用一个全局的WeakMap结构存储所有的 promise 和对应上下文信息
const PromiseCtxsMap = new WeakMap()
// 创建一个promise上下文对象
function createPromiseCtx (resolve, reject) {
return {
resolve,
reject,
// 存储关联 then 函数的上下信息
thenCtxs: []
}
}
// 设置promise上下文对象
function setPromiseCtx(promise, resolve, reject) {
const obj = {
resolve,
reject,
}
if(!PromiseCtxsMap.has(promise)){
PromiseCtxsMap.set(promise, createPromiseCtx())
}
const ctx = PromiseCtxsMap.get(promise)
for(let k of Object.keys(obj)){
if(obj[k]){
ctx[k] = obj[k]
}
}
}
function MyPromise(executor) {
// 判断执行器函数
if (typeof executor !== 'function') {
throw new TypeError(`Promise resolver ${executor} is not a function`)
}
// 拿到promise对象
const promise = this
// 定义存储promise的状态和结果的变量
let PromiseState = PENDING
let PromiseResult = undefined
// 通过 defineProperty 让 状态和结果 暴露出去
Reflect.defineProperty(promise, 'PromiseState', {
configurable: false,
enumerable: false,
get() {
return PromiseState
},
})
Reflect.defineProperty(promise, 'PromiseResult', {
configurable: false,
enumerable: false,
get() {
return PromiseResult
},
})
// 定义 resolve 和 reject
const resolve = function (value) {
// 条件检查
if(PromiseState !== PENDING){
return
}
// 修改状态
PromiseState = FULFILLED
PromiseResult = value
}
const reject = function (reason) {
if(PromiseState !== PENDING){
return
}
PromiseState = REJECTED
PromiseResult = reason
};
// 存储这个promise的 resolve函数和reject
setPromiseCtx(promise, resolve, reject)
// 同步 执行 执行器函数 报错时直接reject这个promise
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
return promise
}
关于状态的操作都已经实现了。只有resolve和reject函数可以更改promise的状态。接下来设置then函数的一些行为。
- 再同一个promise上可以多次调用。
- 返回一个promise。
// 创建一个then函数的上下文
function createThenCtx(onFulfilled, onRejected, next) {
return {
// fulfilled状态的回调 非必需
onFulfilled,
// rejected状态的回调 非必需
onRejected,
// 当前then函数返回的新的promise对象
next,
}
}
// 关联then函数的上下文到对应的promise 的 thenCtxs上
function addThenCtx(promise, thenCtx){
const { PromiseState } = promise
const { thenCtxs } = PromiseCtxsMap.get(promise)
}
MyPromise.prototype.then = function(onFulfilled, onRejected){
// 拿到前一个promise
const promise = this
// 生成一个新的pending状态的promise: next
const next = new MyPromise(() => {})
// 处理promise的回调 如果onFulfilled,onRejected不是函数就忽略
if (typeof onFulfilled !== 'function') {
onFulfilled = undefined
}
if (typeof onRejected !== 'function') {
onRejected = undefined
}
// 生成当前then 函数的一些上下文 信息。
const thenCtx = createThenCtx(onFulfilled, onRejected, next)
// 因为同一个promise可以多次调用then,所以把这个promise所有的thenCtx都关联到promise上下文的thenCtxs, 等后面一起做处理
addThenCtx(promise, thenCtx)
// 最后立即返回一个新的promise
return next
}
then函数行为的大致方向已经完成。接下来就是完善细节,处理promise状态变更的执行对应的回调。
// 处理thenCtxs的函数
function flushThenCtxs(promise){
// 提前解构promise的状态和结果,以及 thenCtxs
const { PromiseState, PromiseResult } = promise
const { thenCtxs } = PromiseCtxsMap.get(promise)
while(thenCtxs.length > 0){
// 从数组第一个依次弹出处理
const thenCtx = thenCtxs.shift()
// 拿到当时then函数存入的回调和新的promise
const { onFulfilled, onRejected, next } = thenCtx
const { resolve, reject } = PromiseCtxsMap.get(next)
// 异步处理onFulfilled, onRejected, 然后再根据结果进入promsie 解决程序
setTimeout(() => {
try {
switch (PromiseState){
case FULFILLED:
if(!onFulfilled){
return resolve(PromiseResult)
}
resolvePromise(next, onFulfilled(PromiseResult))
break
case REJECTED:
if(!onRejected){
return reject(PromiseResult)
}
resolvePromise(next, onRejected(PromiseResult))
break
}
} catch (error) {
reject(error)
}
}, 0)
}
}
// 对 resolve, reject, addThenCtx添加处理thenCtxs的逻辑
function MyPromise(executor) {
// ...other code
const resolve = function (value) {
// ...other code
// 状态变更需要处理thenCtxs
flushThenCtxs(promise)
}
const reject = function (reason) {
// ...other code
// 状态变更需要处理thenCtxs
flushThenCtxs(promise)
}
}
function addThenCtx(promise, thenCtx){
// ...other code
// 如果promise状态已经被敲定了,那么每一次添加addThenCtx都要立即flushThenCtxs
if(PromiseState !== PENDING){
flushThenCtxs(promise)
}
}
最后来实现promsie 解决程序
function resolvePromise(promise, x){
const { resolve, reject } = PromiseCtxsMap.get(promise)
// 同一promise报错
if(promise === x) {
reject(new TypeError(`It's result be the same promise`))
}
// 如果 x 也是 promise,则promise直接关联x的状态
if(x instanceof MyPromise){
// 生成一个新的 thenCtx上下文, 关联到 x 的 thenCtxs
// onFulfilled函数为直接返回值,onRejected为promise的reject
// 行为有点类似于 x.then(y => y, reject), 但是这样会产生一个新的promise
const thenCtx = createThenCtx(y => y, reject, promise)
addThenCtx(x, thenCtx)
} else if(x !== null && (typeof x === 'object' || typeof x === 'function')){
// 如果x是对象或者函数
let then
// 防止检索x.then报错
try {
then = x.then
} catch (error) {
reject(error)
}
// 不存在then直接用x resolve promise
if(typeof then !== 'function'){
return resolve(x)
}
// 防止多次调用
let called = false
// 立即调用then, 用x作为this,根据规范要求走就行了
try {
then.call(x, function(y){
if(called) return;
called = true
resolvePromise(promise, y)
},function(r){
if(called) return;
called = true
reject(r)
})
} catch (error) {
if(called) return;
reject(error)
}
} else {
resolve(x)
}
}
通过 promises-aplus-tests 验证 Mypromise。
package.json
"scripts": {
"test": "promises-aplus-tests index.js"
},
"devDependencies": {
"promises-aplus-tests": "^2.1.2"
}
在index.js最后添加适配器的代码
var promisesAplusTests = require("promises-aplus-tests");
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
promisesAplusTests(MyPromise, function (err) {
// All done; output is in the console. Or check `err` for number of failures.
});
最后直接执行命令,就可以在控制台看到结果
npm run test