本文由浅入深从promise的基本功能开始分析,到最终实现一个可以通过Promise/A+规范872个测试用例的Promise。
初探 promise
export const getData = () => post('xxxx/xxx');
getData().then(res => {
// vue
this.dataList = res.data;
// react
setData(res.data)
}, err => {
})
function post(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({data: [1,2,3]})
}, 1000)
})
}
从以上调用中可以发现以下规则:
Promise是一个构造函数;
Promise接受一个函数作为参数,这个函数的参数,是两个函数(resolve,reject)
Promise返回一个对象,这个对象包含一个then函数,这个then函数,接收两个参数,这两个参数,也都是函数。
Promise的status:
pending
初始的状态,可以改变
一个
Promise在resolve或者reject之前,都处于这个状态我们可以通过调用
resolve或者reject方法,让这个Promise变成fulfilled或者rejected的状态。
fulfilled
不可变状态
在
resolve之后,变成这个状态,拥有一个value
rejected
不可变状态
在
reject之后,变成这个状态,拥有一个reason
then函数
参数
onFulfilled,onRejected必须是函数类型,如果不是,应该被忽略;
onFulfilled和onRejected的特性
在
promise变成fulfilled/rejected状态的时候,应该调用onFulfilled/onRejected;在
promise变成fulfilled/rejected状态之前,不应该被调用只能调用一次。
实现如下:
function LPromise(execute) {
this.status = "pending";
this.value = null;
this.reason = null;
const resolve = (value) => {
if(this.status === "pending") {
this.value = value;
this.status = "fulfilled";
}
}
const reject = (reason) => {
if(this.status === "pending") {
this.reason = reason;
this.status = "rejected";
}
}
try {
execute(resolve, reject);
} catch (error) {
reject(error)
}
}
LPromise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
if(this.status === "fulfilled") {
onFulfilled(this.value);
}
if(this.status === "rejected") {
onRejected(this.reason);
}
}
Promise进阶
但是此时并没有实现异步功能,对于以下代码then中并没有打印出什么:
new LPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello luyi');
}, 1000)
}).then(res => {
console.log(res)
})
这是因为我们在resolve执行的时候,then函数已经执行过了。所以我们如果要实现异步执行,就需要在等执行resolve或者reject的时候去执行then。也就是resolve / reject 执行了之后,再执行 onfulfilled 和 onjected。而且根据Promise/A+规范:2.2.4.onFulfilled 和 onRejected 只有在完成环境堆栈仅包含平台代码时才可被调用,也就是onFulfilled和onJected需要在当前同步任务执行完后执行,所以我们用queueMicrotask方法执行,这也是为什么不管execute中是否有异步,then中的回调函数总会在同步代码执行完后的原因:
function LPromise(execute) {
this.status = "pending";
this.value = null;
this.reason = null;
// 数组是因为then方法可以多次注册,多次调用
this.onFulfilledArray = [];
this.onRejectedArray = [];
const resolve = (value) => {
if(this.status === "pending") {
this.value = value;
this.status = "fulfilled";
this.onFulfilledArray.forEach(func => func(value))
}
}
const reject = (reason) => {
if(this.status === "pending") {
this.reason = reason;
this.status = "rejected";
this.onRejectedArray.forEach(func => func(reason))
}
}
try {
execute(resolve, reject);
} catch (error) {
reject(error)
}
}
LPromise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
if(this.status === "fulfilled") {
queueMicrotask(() => {
onFulfilled(this.value);
})
}
if(this.status === "rejected") {
queueMicrotask(() => {
onRejected(this.reason);
})
}
if(this.status === "pending") {
this.onFulfilledArray.push(onFulfilled);
this.onRejectedArray.push(onRejected);
}
}
此时我们解决了异步调用的问题,可以发现使用的是发布订阅模式,但对于如下代码发现并不如预期结果:
new LPromise((resolve, reject) => {
setTimeout(() => {
resolve('hello ln');
console.log("settimeout")
}, 1000)
}).then(res => {
console.log(res); // hello ln
return res + "ln"
}).then(res => {
console.log(res) // hello ln ln
})
所以我们还要实现promise的链式调用功能。
Promise的链式调用
then 方法,应该返回一个 Promise
promise2 = promise1.then(onFulfilled, onRejected)
onFulfilled / onRejected 的执行结果,为 x, 调用 resolvePromise
如果 onFulfilled / onRejected 执行时抛出异常,我们 promise2 需要被 reject
如果 onFulfilled / onRejected 不是一个函数,promise2 以 promise1 的 value 或者 reason 触发 fulfilled 和 rejected
promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)。
function LPromise(execute) {
this.status = "pending";
this.value = null;
this.reason = null;
this.onFulfilledArray = [];
this.onRejectedArray = [];
const resolve = (value) => {
if(this.status === "pending") {
this.value = value;
this.status = "fulfilled";
this.onFulfilledArray.forEach(func => func(value))
}
}
const reject = (reason) => {
if(this.status === "pending") {
this.reason = reason
this.status = "rejected";
this.onRejectedArray.forEach(func => func(reason))
}
}
// try catch
execute(resolve, reject);
}
LPromise.prototype.then = function(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === "function" ? onFulfilled: (data) => { return data };
onRejected = typeof onRejected === "function" ? onRejected: (error) => { throw error };
let promise2;
if(this.status === "fulfilled") {
return promise2 = new LPromise((resolve, reject) => {
queueMicrotask(() => {
try {
// promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
let result = onFulfilled(this.value);
resolve(result);
} catch(e) {
reject(e)
}
})
})
}
if(this.status === "rejected") {
return promise2 = new LPromise((resolve, reject) => {
queueMicrotask(() => {
try {
// promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
let result = onRejected(this.reason);
resolve(result);
} catch(e) {
reject(e)
}
})
})
}
if(this.status === "pending") {
return promise2 = new LPromise((resolve, reject) => {
this.onFulfilledArray.push(() => {
try {
let result = onFulfilled(this.value);
resolve(result);
} catch(e) {
reject(e)
}
});
this.onRejectedArray.push(() => {
try {
// promise1 中 onfulfilled 返回了一个值,这个值需要被 promise2 进行 resolve ,才能出现在下一个 then(res)
let result = onRejected(this.reason);
resolve(result);
} catch(e) {
reject(e)
}
});
})
}
}
此时已经解决了promise最重要的异步和链式调用的问题,但还不够,根据规范在then中返回一个值时,需要运行promise的解决过程:
resolvePromise规范
/*
* @param x then中返回的值
* @param promise2 返回的promise
* @param resolve,rject 返回的promise的resolve,reject方法
/*
resolvePromise(promise2, x, resolve, reject)
规则
如果 promise2 和 x 相等,那么 reject error;
如果 x 是一个 promise
如果 x 是一个pending 状态,那么 promise2 必须要再 pending, 直到 x 变成 fulfilled / rejected
如果 x 被 fulfilled, fulfill promise with the same value
如果 x 被 rejected, reject promise with the same reason
如果 x 是一个 object 或者 function
Let thenable = x.then
如果 x.then 这一步出错,那么 reject promise with e as the reason
如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromiseFn)
resolvePromiseFn 的入参是y, 执行
resolvePromise(promise2, y, resolve, reject)rejectPromiseFn 的入参是 r, reject promise with r
如果 resolvePromiseFn 和 rejectPromiseFn 都调用了,那么第一个调用优先,后面的忽略
如果调用then 抛出异常
如果 resolvePromise 或 rejectPromise 已经被调用,可以忽略
如果 then 不是一个 function, fulfill promise with x
以下是完整代码:
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function LPromise(executor) {
this.status = PENDING
this.value = null
this.reason = null
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFulfilledCallbacks.forEach(fun => fun(value))
}
}
const reject = (value) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = value
this.onRejectedCallbacks.forEach(fun => fun(value))
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('error due to circular reference'))
}
let consumed = false
let thenable
let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && target !== null
// promise或者类似promise结构的对象都会走此
if (isComplexResult(x)) {
try {
thenable = x.then
if (typeof thenable === 'function') {
thenable.call(
x,
function (value) {
if (consumed) {
return
}
consumed = true
return resolvePromise(promise, value, resolve, reject)
},
function (error) {
if (consumed) {
return
}
consumed = true
return reject(error)
}
)
} else {
resolve(x)
}
} catch (error) {
if (consumed) {
return
}
reject(error)
}
} else {
resolve(x)
}
}
LPromise.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
onRejected = typeof onRejected === 'function' ? onRejected : (error) => { throw error }
let promise2;
if (this.status === FULFILLED) {
return promise2 = new LPromise((resolve, reject) => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.status === REJECTED) {
return promise2 = new LPromise((resolve, reject) => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.status === PENDING) {
return promise2 = new LPromise((resolve, reject) => {
this.onFulfilledCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
this.onRejectedCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
})
}
}
最后用官方测试用例跑一下,全部通过:

1598

被折叠的 条评论
为什么被折叠?



