promise 简化版
promise 关键是状态管理,对应三个状态,等待、成功、失败。
实例化 promise 示例时传入一个函数,这个函数会直接在主线程中执行,该函数会提供两个参数:resolve、reject,也是函数,作用是改变实例化的这个 promise 的状态,resolve 把状态改变成成功,reject 改变成失败。
当存在异步情况时,可以通过这两个函数改变状态,后续的 then 方法会根据 promise 的状态来执行。两个函数都可以传入一个参数,分别作为成功结果和失败原因。
于是有下边代码
js">const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
constructor(executor) {
this.state = PENDING
this.value = undefined
this.reason = undefined
let resolve = value => {
this.state = FULFILLED
}
let reject = reason => {
this.state = REJECTED
}
try {
executor(resolve)
} catch (error) {
reject(error)
}
}
}
promise 实例有一个 then 方法,方法里传入两个回调函数: onFulfill, onReject,分别是该 promise 状态变为成功、失败后触发的函数。
调用 then 方法时该 promise 实例对应上边三个状态中的一个,状态是成功时直接调用 onFulfill(会放到微任务里,不在主线程),失败时调用 onReject,等待时则把回调存下来,当 promise 状态改变,即调用 resolve、reject 两个函数时,再执行相应的回调。
js">class Promise {
constructor(executor) {
this.state = PENDING
this.value = undefined
this.reason = undefined
this.onFulfillCallbacks = []
this.onRejectCallbacks = []
let resolve = value => {
this.state = FULFILLED
this.value = value
this.onFulfillCallbacks.forEach(f => f(this.value))
}
let reject = reason => {
this.state = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(f => f(this.reason))
}
try {
executor(resolve)
} catch (error) {
reject(error)
}
}
then(onFulfill, onReject) {
if (this.state == PENDING) {
this.onFulfillCallbacks.push(value => onFulfill(value))
this.onRejectCallbacks.push(reason => onReject(reason))
}
if (this.state == FULFILLED) {
onFulfill(this.value)
}
if (this.state == REJECTED) {
onReject(this.reason)
}
}
}
现在这个 promise 示例已经有初步状态管理的意思了,但是 promise 是可以链式调用的,也就是说 then 返回的是 promise,修改 then 方法
js">
class Promise {
// ...
then(onFulfill, onReject) {
let p;
if (this.state == PENDING) {
p = new Promise((resolve, reject) => {
this.onFulfillCallbacks.push(value => onFulfill(value))
this.onRejectCallbacks.push(reason => onReject(reason))
})
}
if (this.state == FULFILLED) {
p = new Promise((resolve, reject) => {
onFulfill(this.value)
})
}
if (this.state == REJECTED) {
p = new Promise((resolve, reject) => {
onReject(this.reason)
})
}
return p;
}
}
现在 then 方法是返回 promise了,看似可以链式调用了,但是这个返回的 promise 并没有状态管理,一直是在 pending 状态,怎么改变它的状态就是难点。
返回的 promise 应当在 then 方法里传入的两个参数 onFulfill、onReject执行时改变状态。 把改变它的状态封装成一个函数 resolvePromise,
onFulfill、onReject 返回的值可能是 promise 或者其他类型的值,onFulfill、onReject执行后说明返回的promise 已经相应,就是需要改变它的状态,也就是resolvePromise 需要接受4个参数, onFulfill/onReject 的结果、返回的promise ,resolve,reject。resolve,reject 函数来改变返回的 promise 的状态。
js">class Promise {
// ...
then(onFulfill, onReject) {
let p;
if (this.state == PENDING) {
p = new Promise((resolve, reject) => {
this.onFulfillCallbacks.push(value => {
try {
let data = onFulfill(value)
resolvePromise(data, p, resolve, reject);
} catch (error) {
reject(error)
}
})
this.onRejectCallbacks.push(reason => {
try {
let data = onReject(reason)
resolvePromise(data, p, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.state == FULFILLED) {
p = new Promise((resolve, reject) => {
try {
let value = onFulfill(this.value)
resolvePromise(value, p, resolve, reject);
} catch (error) {
reject(error)
}
})
}
if (this.state == REJECTED) {
p = new Promise((resolve, reject) => {
try {
let reason = onReject(this.reason)
resolvePromise(reason, p, resolve, reject)
} catch (error) {
reject(error)
}
})
}
return p;
}
}
然后是 resolvePromise 的实现,如果 onFulfill/onReject 返回的是正常的值,就调用 resolve 方法改变返回的 promise 的状态,报错了就 调用 reject
如果返回的是 promise,则调用 then 方法拿到结果值,递归调用该方法,
js">function resolvePromise(result, p, resolve, reject) {
// 根据规范 onFulfill/onReject 返回的不能是 then 返回的 promise 本身。
if (result === p) {
throw new TypeError('cycle')
}
if (result && result.then && typeof result.then == 'function') {
// 结果是一个 promise
result.then(value => {
resolvePromise(value, p, resolve, reject)
}, reason => {
reject(reason)
})
} else {
resolve(result)
}
}
目前代码如下
js">const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
constructor(executor) {
this.state = PENDING
this.value = undefined
this.reason = undefined
this.onFulfillCallbacks = []
this.onRejectCallbacks = []
let resolve = value => {
this.state = FULFILLED
this.value = value
this.onFulfillCallbacks.forEach(f => f(this.value))
}
let reject = reason => {
this.state = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(f => f(this.reason))
}
try {
executor(resolve)
} catch (error) {
reject(error)
}
}
then(onFulfill, onReject) {
let p;
if (this.state == PENDING) {
p = new Promise((resolve, reject) => {
this.onFulfillCallbacks.push(value => {
try {
let data = onFulfill(value)
resolvePromise(data, p, resolve, reject);
} catch (error) {
reject(error)
}
})
this.onRejectCallbacks.push(reason => {
try {
let data = onReject(reason)
resolvePromise(data, p, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
if (this.state == FULFILLED) {
p = new Promise((resolve, reject) => {
try {
let value = onFulfill(this.value)
resolvePromise(value, p, resolve, reject);
} catch (error) {
reject(error)
}
})
}
if (this.state == REJECTED) {
p = new Promise((resolve, reject) => {
try {
let reason = onReject(this.reason)
resolvePromise(reason, p, resolve, reject)
} catch (error) {
reject(error)
}
})
}
return p;
}
}
function resolvePromise(result, p, resolve, reject) {
// 根据规范 onFulfill/onReject 返回的不能是 then 返回的 promise 本身。
if (result === p) {
throw new TypeError('cycle')
}
if (result && result.then && typeof result.then == 'function') {
// 结果是一个 promise
result.then(value => {
resolvePromise(value, p, resolve, reject)
}, reason => {
reject(reason)
})
} else {
resolve(result)
}
}
promise 完整版
这里,基本一个 promise 应有的功能就完成了。这里只是按照思路写了下来,通过 promises-aplus-tests
测试,还有很多不符合规范的地方,比如
- then 里的两个回调是应放在微任务里,现在可能在主进程执行
- 没处理 then 传入的参数不是函数的情况
- 没有考虑 resolve/reject 两个函数重复调用的问题
- resolvePromise 判断结果值是不是 promise 不准确
- …
完善代码后如下,以下代码经过 promises-aplus-tests
包测试。
js">const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 有三种状态 resolve 会把状态改成fulfilled reject 会把状态改成rejected
class Promise {
constructor(executor) {
this.state = PENDING;
this.value = undefined;
this.reason = undefined;
this.onFulfillCallbacks = [];
this.onRejectCallbacks = []
let isCalled = false;
let resolve = (value) => {
if (isCalled) return;
isCalled = true;
this.state = FULFILLED;
this.value = value
this.onFulfillCallbacks.forEach(c => {
if (isFunction(c)) c(this.value);
})
// 要执行then方法参数里的成功回调
}
let reject = reason => {
if (isCalled) return;
isCalled = true;
this.state = REJECTED
this.reason = reason
this.onRejectCallbacks.forEach(c => {
isFunction(c) && c(reason)
})
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfill, onReject) {
let p;
// 这两个值不是函数 则忽略 传给下一个then
if (!isFunction(onFulfill)) {
onFulfill = r => r
}
if (!isFunction(onReject)) {
onReject = r => {
// 不是函数的话把错误留给下一个,不能直接返回r 不然状态就是fulfill的了
throw r
}
}
// 判断当前状态
if (this.state == PENDING) {
// 这里的promise是要返回的 但是then里的回调在this里,不在p
// 当旧的promise resovle时,执行onFulfill
p = new Promise((resolve, reject) => {
// ?onFulfillCallback是刚初始的那个吗 是的
// 因为知不道之前promise 执行resolve后返回的什么值,所以需要判断一下返回的是promise则需要等待完成,完成后调用这个promise的resolve
this.onFulfillCallbacks.push(() => {
// 调用resolve后 then里的函数是微任务 只能用settimeout宏任务模拟了
setTimeout(() => {
// 当之前的promise resolve后,会调用这里的方法
// then里的onFulfill 传的参数为当前结果值 this.value 在执行到时才确定值
try {
let result = onFulfill(this.value);
resolvePromise(result, p, resolve, reject);
} catch (error) {
reject(error)
}
}, 0);
});
this.onRejectCallbacks.push(() => {
setTimeout(() => {
try {
let reason = onReject(this.reason);
resolvePromise(reason, p, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
});
})
}
if (this.state == FULFILLED) {
p = new Promise((resolve, reject) => {
// 直接resolve后的then函数也是微任务 延迟执行 不加settimeout 这个函数会在主线程里执行
setTimeout(() => {
// onFulfill onReject 报错的话 该promise 就变成reject状态
try {
let result = onFulfill(this.value)
resolvePromise(result, p, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
}
if (this.state == REJECTED) {
p = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let reason = onReject(this.reason);
resolvePromise(reason, p, resolve, reject)
} catch (error) {
reject(error)
}
}, 0);
})
}
return p;
}
catch (onRejected) {
// catch 方法就是then方法没有成功的简写
return this.then(null, onRejected);
}
}
Promise.resolve = function(value) {
return new Promise(resolve => {
resolve(value);
})
}
Promise.reject = function(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
Promise.all = function(promises) {
if (!Array.isArray(promises)) return;
let currTimes = 0;
let allTimes = promises.length;
let results = [];
return new Promise((resolve, reject) => {
promises.forEach((p, i) => {
p.then(data => {
currTimes++;
results[i] = data;
if (currTimes == allTimes) {
resolve(results)
}
}, reject)
})
})
}
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
// 不用担心重复调用,因为resolve只会调用一次
p.then(resolve, reject)
})
})
}
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd
}
function isFunction(p) {
return typeof p == 'function';
}
/**
* 拿到上个promise的结果,判断各个可能的类型,然后执行新的promise的resolve或者rejected
* @param {any} result 上个promise resolve的结果
* @param {promise} p 新的promise
* @param {function} resolve 上一个promise完成后 紧接着就调用resolve或者reject
* @param {function} reject
*/
function resolvePromise(result, p, resolve, reject) {
if (result === p) {
throw new TypeError('recyle promise')
}
// then 方法可能是自己创建的对象 里边第一个参数可能执行多次,即onFulfill方法,规范里只能执行一次,需要防止。 如 {then(res) {res();res();}}
let isCalled = false;
try {
// 不能这么写 if (result) { 这样写 其他的基本类型如果有then方法,就会走进去,实际不应该走下去 比如Bollean.prototype.then=y=>y;传入个true会走进if里
if ((typeof result == 'function' || typeof result == 'object') && result !== null) {
// 测试里result.then只能get一次
let then = result.then;
if (isFunction(then)) {
// 返回的是promise 就需要拿到该promise的返回值,知道不是promise,promise 完成后会调resolve方法,所以在then里拿到可以拿到结果
then.call(result, value => {
if (isCalled) return;
isCalled = true;
// 拿到的value可能还是promise 递归调用直到不是promise
resolvePromise(value, p, resolve, reject)
}, reason => {
// 失败了就直接reject
if (isCalled) return;
isCalled = true;
reject(reason)
})
} else {
resolve(result)
}
} else {
// 普通值
resolve(result)
}
} catch (error) {
if (isCalled) return;
isCalled = true;
reject(error)
}
}