相信做为一名JSer,你们现在确定都对Promise的使用很是熟悉了。Promise的出现,大大改善了js代码『回调地狱』的问题,再结合async/await等语法特性,可让JS书写简洁优美、可读性高的异步代码。前端
在Promise规范化的路上,社区的贡献可谓相当重要。早期各类版本的Promise库最终推进了Promises/A+规范的达成,并最终被归入语言规范。现在随着async/await的出现,使得JS中的异步编程变得更加优雅、简洁。node
今天我准备和你们一块儿,尝试本身动手实现一个简略版本的Promise的polyfill。git
咱们的Promise将处于如下三种状态中的一种:github
PENDING状态能够转换为FULFILLED或REJECTED状态,然后二者不能再次转换。编程
const STATUS = {
PENDING: Symbol('PENDING'),
FULFILLED: Symbol('FULFILLED'),
REJECTED: Symbol('REJECTED'),
}
复制代码
在Promise构造函数中,咱们会初始化一些属性:将状态置为PENDING,初始化回调数组。数组
咱们将接收一个函数做为参数executor
,随后这个函数将当即被调用,同时传入两个函数做为参数,调用这两个函数将分别resolve和reject当前promise:promise
class Promise {
constructor(executor) {
this.status = STATUS.PENDING;
this.handlers = [];
this._resolveFromExecutor(executor);
}
}
复制代码
接着咱们执行executor,要注意的是执行时咱们要使用try/catch,若是发生异常,则使用抛出的异常reject当前promise。executor也能够主动resolve或reject当前promise:bash
_resolveFromExecutor(executor) {
const r = this._execute(executor, (value) => {
this._resolveCallback(value);
}, (reason) => {
this._rejectCallback(reason);
});
if (r !== undefined) {
this._rejectCallback(r);
}
}
复制代码
_resolveCallback与_rejectCallback都须要率先判断当前promise的状态是否为PENDING:若状态非PENDING则直接忽视调用;不然设置状态为FULFILLED或REJECTED,而且将值或拒绝缘由记录下来,同时异步处理回调:app
_resolveCallback(value) {
if (this.status !== STATUS.PENDING) return;
return this._fulfill(value);
}
_fulfill(value) {
if (this.status !== STATUS.PENDING) return;
this.status = STATUS.FULFILLED;
this._value = value;
async.settlePromises(this);
}
_rejectCallback(reason) {
this._reject(reason);
}
_reject(reason) {
if (this.status !== STATUS.PENDING) return;
this.status = STATUS.REJECTED;
this._reason = reason;
async.settlePromises(this);
}
复制代码
这里的async.settlePromises会异步调用promise._settlePromises。异步
js有许多异步执行的方式,包括简单的setTimeout、requestAnimationFrame,node环境下的nextTick、setImmediate,还有一些方法好比利用图片加载error或是MutationObserver等等。这里偷个懒直接用setTimeout了。
const async = {
schedule(fn) {
setTimeout(fn, 0);
},
settlePromises(promise) {
this.schedule(() => {
promise._settlePromises();
});
},
};
复制代码
_settlePromises将逐个执行handlers数组中注册的回调,并在此后清空handlers数组。在此实现_settlePromises方法以前,先来看看是如何向handlers数组添加回调的。
then与cacth将调用_addCallbacks向handlers数组添加回调:
_addCallbacks(fulfill, reject, promise) {
this.handlers.push({
fulfill,
reject,
promise,
});
}
复制代码
而then与catch是对私有方法_then的进一步包装:
then(didFulfill, didReject) {
return this._then(didFulfill, didReject);
}
catch(fn) {
return this.then(undefined, fn);
}
复制代码
每当调用_then方法将生成一个新的promise实例并返回:
_then(didFulfill, didReject) {
const promise = new Promise(INTERNAL);
let handler;
let value;
this._addCallbacks(didFulfill, didReject, promise);
return promise;
}
复制代码
这里咱们传入的executor将不会调用resolve或reject改变promise状态,而是将其加入父级promise的handlers数组并在父级_settlePromises时处理,由此造成了promise链:
parentPromise.then -> 生成childPromise并返回 -> 加入parentPromise的handlers
parentPromise._settlePromises -> 执行childPromise的_fulfill或_reject
复制代码
_settlePromises会遍历handlers并调用_settlePromise。若是_then加入了回调函数,那咱们须要调用这个函数并根据其结果去resolve或reject目标promise;不然直接用本来的结果来resolve或reject目标promise:
_settlePromises() {
this.handlers.forEach(({ fulfill, reject, promise }) => {
if (this.status === STATUS.FULFILLED) {
this._settlePromise(promise, fulfill, this._value);
} else {
this._settlePromise(promise, reject, this._reason);
}
});
this.handlers.length = 0;
}
_settlePromise(promise, handler, value) {
if (typeof handler === 'function') {
this._settlePromiseFromHandler(handler, value, promise);
} else {
if (promise.status === STATUS.FULFILLED) {
promise._fulfill(value);
} else {
promise._reject(value);
}
}
}
_settlePromiseFromHandler(handler, value, promise) {
const x = tryCatch(handler).call(null, value);
if (x === errorObj) {
promise._reject(x.e);
} else {
promise._resolveCallback(x);
}
}
复制代码
接着添加两个静态方法,返回一个promise示例,并马上用传入的值resolve或reject这个promise。
Promise.resolve = function resolve(v) {
return new Promise((res) => {
res(v);
});
};
Promise.reject = function reject(v) {
return new Promise((_, rej) => {
rej(v);
});
};
复制代码
固然,以上的代码并不会正确运行。
首先咱们来看一下_then方法。咱们须要判断当前promise是不是PENDING状态:若是是则将回调加入handlers数组;不然当即执行回调:
const async = {
...
invoke(fn, receiver, arg) {
this.schedule(() => {
fn.call(receiver, arg);
});
},
};
_then(didFulfill, didReject) {
const promise = new Promise(INTERNAL);
const target = this;
let handler;
let value;
if (target.status !== STATUS.PENDING) {
if (target.status === STATUS.FULFILLED) {
handler = didFulfill;
value = target._value;
} else if (target.status === STATUS.REJECTED) {
handler = didReject;
value = target._reason;
}
async.invoke(
function ({ promise, handler, value }) {
this._settlePromise(promise, handler, value);
},
target,
{
handler,
promise,
value,
}
);
} else {
target._addCallbacks(didFulfill, didReject, promise);
}
return promise;
}
复制代码
接下来还有一个问题要处理,若是一个promise被另外一个promise所resolve,则须要进行特别的处理。
若是做为值的promise已经非PENDING状态,那比较简单,直接用它的结果resolve或reject当前的promise便可。若是目标promise还在PENDING状态,则将当前的promise以及它的handlers转交给目标promise。由于当前的promise可能也被做为其余promise的resolve的值,所以这里也要维护一个上级状态,以便找到链的最前端:
_resolveCallback(value) {
if (this.status !== STATUS.PENDING) return;
if (!(value instanceof Promise)) return this._fulfill(value);
const p = value._target();
if (p.status === STATUS.PENDING) {
const len = this.handlers.length;
this.handlers.forEach(({ fulfill, reject, promise }) => {
p._addCallbacks(fulfill, reject, promise);
});
this._isFollowing = true;
this.handlers.length = 0;
this._followee = p;
} else if (p.status === STATUS.FULFILLED) {
this._fulfill(p._value);
} else if (p.status === STATUS.REJECTED) {
this._reject(p._reason);
}
}
_target() {
let ret = this;
while (ret._isFollowing) ret = ret._followee;
return ret;
}
复制代码
同时当咱们调用promise._then时,也须要使用这个追溯机制:
_then(didFulfill, didReject) {
const promise = new Promise(INTERNAL);
const target = this;
...
}
复制代码
最后咱们实现一下Promise.all。这里的思路很简单,生成一个promise示例,对传入的数组中的全部promise用then监听结果,若是所有resolve则用全部结果组成的数组resolve返回的promise,有一个失败则当即用这个错误reject:
class PromiseArray {
constructor(values, count, isAll) {
this._ps = values;
this._count = isAll ? values.length : count;
this._isAll = isAll;
this._values = [];
this._valueCount = 0;
this._reasons = [];
this._reasonCount = 0;
this._promise = new Promise(INTERNAL);
this._iterate();
}
_iterate() {
let p;
for (let i = 0; i < this._ps.length; i++) {
p = this._ps[i];
p.then(function (index, value) {
if (this._isAll) {
this._values[index] = value;
} else {
this._values.push(value);
}
this._valueCount++;
this._check();
}.bind(this, i), function (index, reason) {
if (this._isAll) {
this._reasons[index] = reason;
} else {
this._reasons.push(reason);
}
this._reasonCount++;
this._check();
}.bind(this, i));
}
}
_check() {
if (this._count <= this._valueCount) {
this._promise._fulfill(this._values);
} else if (this._ps.length - this._count < this._reasonCount) {
this._promise._reject(this._reasons);
}
}
}
Promise.all = function (values) {
return new PromiseArray(values, undefined, true)._promise;
};
复制代码
实现Promise的关键点在于如何实现Promise链。
使用Promise以及async/await将大大提升代码的可读性、下降复杂度。
(function () {
const errorObj = {};
let tryCatchTarget;
const tryCatcher = function tryCatcher() {
try {
const target = tryCatchTarget;
tryCatchTarget = null;
return target.apply(this, arguments);
} catch (e) {
errorObj.e = e;
return errorObj;
}
};
const tryCatch = function tryCatch(fn) {
tryCatchTarget = fn;
return tryCatcher;
};
const async = {
schedule(fn) {
setTimeout(fn, 0);
},
invoke(fn, receiver, arg) {
this.schedule(() => {
fn.call(receiver, arg);
});
},
settlePromises(promise) {
this.schedule(() => {
promise._settlePromises();
});
},
};
const INTERNAL = function INTERNAL() {};
const STATUS = {
PENDING: Symbol('PENDING'),
FULFILLED: Symbol('FULFILLED'),
REJECTED: Symbol('REJECTED'),
}
class Promise {
constructor(executor) {
this.status = STATUS.PENDING;
this.handlers = [];
this._isFollowing = false;
this._followee = null;
this._resolveFromExecutor(executor);
}
_resolveFromExecutor(executor) {
// if (executor === INTERNAL) return;
const r = this._execute(executor, (value) => {
this._resolveCallback(value);
}, (reason) => {
this._rejectCallback(reason);
});
if (r !== undefined) {
this._rejectCallback(r);
}
}
_execute(executor, resolve, reject) {
try {
executor(resolve, reject);
} catch (e) {
return e;
}
}
_resolveCallback(value) {
if (this.status !== STATUS.PENDING) return;
if (!(value instanceof Promise)) return this._fulfill(value);
const p = value._target();
if (p.status === STATUS.PENDING) {
const len = this.handlers.length;
this.handlers.forEach(({ fulfill, reject, promise }) => {
p._addCallbacks(fulfill, reject, promise);
});
this._isFollowing = true;
this.handlers.length = 0;
this._followee = p;
} else if (p.status === STATUS.FULFILLED) {
this._fulfill(p._value);
} else if (p.status === STATUS.REJECTED) {
this._reject(p._reason);
}
}
_target() {
let ret = this;
while (ret._isFollowing) ret = ret._followee;
return ret;
}
_fulfill(value) {
if (this.status !== STATUS.PENDING) return;
this.status = STATUS.FULFILLED;
this._value = value;
async.settlePromises(this);
}
_rejectCallback(reason) {
this._reject(reason);
}
_reject(reason) {
if (this.status !== STATUS.PENDING) return;
this.status = STATUS.REJECTED;
this._reason = reason;
async.settlePromises(this);
}
then(didFulfill, didReject) {
return this._then(didFulfill, didReject);
}
_then(didFulfill, didReject) {
const promise = new Promise(INTERNAL);
const target = this._target();
let handler;
let value;
if (target.status !== STATUS.PENDING) {
if (target.status === STATUS.FULFILLED) {
handler = didFulfill;
value = target._value;
} else if (target.status === STATUS.REJECTED) {
handler = didReject;
value = target._reason;
}
async.invoke(
function ({ promise, handler, value }) {
this._settlePromise(promise, handler, value);
},
target,
{
handler,
promise,
value,
}
);
} else {
target._addCallbacks(didFulfill, didReject, promise);
}
return promise;
}
catch(fn) {
return this.then(undefined, fn);
}
_addCallbacks(fulfill, reject, promise) {
this.handlers.push({
fulfill,
reject,
promise,
});
}
_settlePromises() {
this.handlers.forEach(({ fulfill, reject, promise }) => {
if (this.status === STATUS.FULFILLED) {
this._settlePromise(promise, fulfill, this._value);
} else {
this._settlePromise(promise, reject, this._reason);
}
});
this.handlers.length = 0;
}
_settlePromise(promise, handler, value) {
if (typeof handler === 'function') {
this._settlePromiseFromHandler(handler, value, promise);
} else {
if (promise.status === STATUS.FULFILLED) {
promise._fulfill(value);
} else {
promise._reject(value);
}
}
}
_settlePromiseFromHandler(handler, value, promise) {
const x = tryCatch(handler).call(null, value);
if (x === errorObj) {
promise._reject(x.e);
} else {
promise._resolveCallback(x);
}
}
}
Promise.resolve = function resolve(v) {
return new Promise((res) => {
res(v);
});
};
Promise.reject = function reject(v) {
return new Promise((_, rej) => {
rej(v);
});
};
window.Promise = Promise;
class PromiseArray {
constructor(values, count, isAll) {
this._ps = values;
this._count = isAll ? values.length : count;
this._isAll = isAll;
this._values = [];
this._valueCount = 0;
this._reasons = [];
this._reasonCount = 0;
this._promise = new Promise(INTERNAL);
this._iterate();
}
_iterate() {
let p;
for (let i = 0; i < this._ps.length; i++) {
p = this._ps[i];
p.then(function (index, value) {
if (this._isAll) {
this._values[index] = value;
} else {
this._values.push(value);
}
this._valueCount++;
this._check();
}.bind(this, i), function (index, reason) {
if (this._isAll) {
this._reasons[index] = reason;
} else {
this._reasons.push(reason);
}
this._reasonCount++;
this._check();
}.bind(this, i));
}
}
_check() {
if (this._count <= this._valueCount) {
this._promise._fulfill(this._values);
} else if (this._ps.length - this._count < this._reasonCount) {
this._promise._reject(this._reasons);
}
}
}
Promise.all = function (values) {
return new PromiseArray(values, undefined, true)._promise;
};
Promise.some = function (values, count) {
return new PromiseArray(values, count, false)._promise;
};
Promise.any = function (values) {
return new PromiseArray(values, 1, false)._promise;
};
})();
复制代码