promise 实现
总览
相关术语及概念见上述链接,以下为 promise
实现:
javascript
const isFunc = (obj) => Object.prototype.toString.call(obj) === '[object Function]';
const isObj = (obj) => Object.prototype.toString.call(obj) === '[object Object]';
// 等待态 规范 2.1.1
const PENDING = 'pending';
// 执行态 规范 2.1.2
const FULFILLED = 'fulfilled';
// 拒绝态 规范 2.1.3
const REJECTED = 'rejected';
class MyPromise {
constructor(fn) {
this.status = PENDING;
this.value = undefined;
this.callbacks = [];
let resolve = (val) => {
this._execCallback(FULFILLED, val);
};
let reject = (reason) => {
this._execCallback(REJECTED, reason);
};
try {
fn(resolve, reject);
} catch (err) {
this._reject(err);
}
}
_execCallback(status, val) {
if (this.status !== PENDING) {
return;
}
this.status = status;
this.value = val;
this.callbacks.forEach((cb) => cb());
}
//规范 2.3 Promise 解决过程 [[Resolve]](promise, x)
_resolvePromise(newPromise, x, resolve, reject) {
if (newPromise === x) {
//规范 2.3.1
return reject(new TypeError('循环引用'));
}
if (x instanceof MyPromise) {
//规范 2.3.2
x.then((y) => {
this._resolvePromise(newPromise, y, resolve, reject);
}, reject);
//规范 2.3.3
} else if (isObj(x) || isFunc(x)) {
if (x === null) {
return resolve(x);
}
let then = undefined;
try {
//规范 2.3.3.1 把 x.then 赋值给 then
then = x.then;
} catch (err) {
//规范 2.3.3.2
return reject(err);
}
//规范 2.3.3.3
if (isFunc(then)) {
let called = false;
try {
then.call(
x,
(y) => {
if (called) return;
called = true;
this._resolvePromise(newPromise, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
},
);
} catch (err) {
if (called) return;
reject(err);
}
} else {
//规范 2.3.3.4
resolve(x);
}
} else {
//规范 2.3.4
resolve(x);
}
}
//规范 2.2 promise.then(onFulfilled, onRejected)
then(onFulfilled, onRejected) {
//规范 2.2.1.1,2.2.7.3
onFulfilled = isFunc(onFulfilled) ? onFulfilled : (val) => val;
// //规范 2.2.1.2,2.2.7.4
onRejected = isFunc(onRejected)
? onRejected
: (err) => {
throw err;
};
const promise2 = new MyPromise((resolve, reject) => {
const fn = () => {
//规范 2.2.4, 2.2.5 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用,且被作为函数调用(即没有 this 值)
setTimeout(() => {
try {
const x = this.status === FULFILLED ? onFulfilled(this.value) : onRejected(this.value);
//规范 2.2.7.1
this._resolvePromise(promise2, x, resolve, reject);
} catch (err) {
//规范 2.2.7.2
reject(err);
}
});
};
// 规范 2.2.6
this.status === PENDING ? this.callbacks.push(fn) : fn();
});
// 规范 2.2.7
return promise2;
}
//catch 方法
catch(onRejected) {
return this.then(null, onRejected);
}
//finally 方法
finally(cb) {
return this.then(
(val) => {
return MyPromise.resolve(cb()).then(() => val);
},
(err) => {
return MyPromise.resolve(cb()).then(() => {
throw err;
});
},
);
}
//resolve 方法
static resolve(params) {
return new MyPromise((resolve) => {
resolve(params);
});
}
//reject 方法
static reject(err) {
return new MyPromise((resolve, reject) => {
reject(err);
});
}
//all 方法
static all(params) {
return new MyPromise((resolve, reject) => {
let count = 0;
let valueList = [];
const promises = Array.from(params);
if (promises.length === 0) {
return resolve(valueList);
}
promises.forEach((item, index) => {
if (!item instanceof MyPromise) {
item = MyPromise.resolve(item);
}
item.then((r) => {
valueList[index] = r;
if (promises.length === ++count) {
resolve(valueList);
}
}, reject);
});
});
}
//race 方法
static race(params) {
return new MyPromise((resolve, reject) => {
const promises = Array.from(params);
if (promises.length === 0) {
return resolve();
}
promises.forEach((item) => {
if (!item instanceof MyPromise) {
item = MyPromise.resolve(item);
}
item.then(resolve, reject);
});
});
}
//用于 promise test
static deferred() {
const result = {};
result.promise = new MyPromise((resolve, reject) => {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
}
module.exports = MyPromise;
promise 测试
手写的 Promise
可通过promises-aplus-tests测试是否符合规范,Promise
内部添加 deferred
静态方法并导出:
js
static deferred() {
const result = {};
result.promise = new MyPromise((resolve, reject) => {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise;
然后执行命令:
bash
promises-aplus-tests MyPromise
chrome 里的实现
实践中要确保 onFulfilled
和 onRejected
方法异步执行,且应该在 then
方法被调用的那一轮事件循环之后的新执行栈中执行。本实现代码采用 setTimeout
(宏任务)来实现异步任务,而 chrome
里的 promise
实现则是采用微任务(%EnqueueMicrotask
)的方式,故略有不同。
chrome
的 promise
实现参考源码版本小于 5.6.100
的 src/js/promise.js 文件,其中 PromiseEnqueue
方法里的 C
函数 %EnqueueMicrotask
将 PromiseHandle
加入到 JS运行时
的微任务队列中。
其中因为 chrome
版本的不断迭代, 在版本 5.6.100 里的 hash
为 6f94a8 的提交里重写了整个 PromiseEnqueue
,然后在后续版本 6.1.395 里的 hash
为 bba473 的提交里完全删除了 promise.js
),最终由 JS
实现完全迭代为 C
开发实现。