前言
异步编程作为JavaScript 中的核心内容,是必须要掌握的一个基础。在日常开发过程中,会有很多异步的场景。比如定时器,网络请求,事件监听等等… 而异步编程中也有很多让人诟病的点,比如 callback hell(回调地狱)。
这个系列是记录下自己平常对异步知识的一些思考和记录。之后还会有对generator函数以及async/await的文章。
正文
这一篇文章不是关注promise API的使用,而是根据promise/A+的规范来实现一个promise。由于涉及到具体实现,需要有一定的JavaScript基础。
规范地址
具体实现
/**
自定义Promise实现,遵循Promise/A+规范
*/
var PENDING = 'pending';
var FULFILLED = 'fulfilled';
var REJECTED = 'rejected';
function Promise(fn){
var self = this;
if (!(this instanceof Promise)){
throw new Error('must use a new operator symbol!');
}
if (!fn) {
throw new Error('a param is required!');
}
if (typeof fn !== 'function') {
throw new Error('the param must be a function!');
}
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
//异步事件队列
this.resolveQuene = [];
this.rejectQuene = [];
// 触发成功回调
var resolveAction = function(param){
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = param;
// 执行成功回调方法队列
for (var i = 0;i < self.resolveQuene.length;i++) {
self.resolveQuene[i] && self.resolveQuene[i]();
}
}
}
// 触发失败回调
var rejectAction = function(reason){
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
// 执行失败回调方法队列
for (var i = 0;i < self.rejectQuene.length;i++) {
self.rejectQuene[i] && self.rejectQuene[i]();
}
}
}
try {
fn.call(null,resolveAction,rejectAction);
} catch(e){
rejectAction(e);
}
}
Promise.resolve = function(param){
return new Promise(function(resolve){
resolve(param);
});
}
Promise.reject = function(reason){
return new Promise(function(resolve,reject){
reject(reason);
});
}
Promise.prototype.catch = function(onRejected){
return this.then(null,onRejected);
}
Promise.all = function(promiseArray){
if (!Array.isArray(promiseArray)) {
throw new Error('param must be a array');
}
var store = [];
return new Promise(function(resolve,reject){
for (var i = 0; i < promiseArray.length; i ++) {
promiseArray[i].then(function(value){
store.push(value);
if (store.length === promiseArray.length) {
resolve(store);
}
},function(reason){
reject(reason);
});
}
});
}
Promise.race = function(promiseArray){
if (!Array.isArray(promiseArray)) {
throw new Error('param must be a array');
}
return new Promise(function(resolve,reject){
for (var i = 0; i < promiseArray.length; i ++) {
promiseArray[i].then(function(value){
resolve(store);
},function(reason){
reject(reason);
});
}
});
}
Promise.assistPromise4resolve = function(promise,x,resolve,reject){
if (x === promise) {
throw new TypeError('chaning Promise error');
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')){
let flag = false;
try {
var then = x.then;
if (typeof then === 'function') {
then.call(x,function(y){
if (flag) return;
flag = true;
Promise.assistPromise4resolve(promise,y,resolve,reject);
},function(reason){
if (flag) return;
flag = true;
reject(reason);
})
} else {
if (flag) return;
flag = true;
resolve(x);
}
} catch(e) {
if (flag) return;
flag = true;
reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype.then = function(onFulfilled,onRejected){
// promise 规范 : 每一个then方法必须要返回一个新的promise以支持链式调用
// 为什么不直接返回一个 this 呢?
// 1.因为promise的状态只能改变一次
// 指向上一个promise的实例
var self = this;
/*
如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因
*/
var nextPromise = new Promise(function(resolve,reject){
if (self.status === FULFILLED) {
// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行
setTimeout(function(){
try {
if (typeof onFulfilled === 'function') {
var innerValue = onFulfilled(self.value);
Promise.assistPromise4resolve(nextPromise,innerValue,resolve,reject);
} else {
resolve(self.value);
}
} catch (e){
reject(e)
}
});
}
if (self.status === REJECTED) {
// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行
setTimeout(function(){
try {
if (typeof onRejected === 'function') {
var innerReason = onRejected(self.reason);
Promise.assistPromise4resolve(nextPromise,innerReason,resolve,reject);
} else {
reject(self.reason);
}
} catch (e) {
reject(e);
}
});
}
// 假如实例化promise时执行的是异步代码,此时状态还是pending
// 为了确保代码的逻辑顺序,规范要求then的回调要异步执行
if (self.status === PENDING) {
self.resolveQuene.push(function(){
setTimeout(function(){
try {
if (typeof onFulfilled === 'function') {
var innerValue = onFulfilled(self.value);
Promise.assistPromise4resolve(nextPromise,innerValue,resolve,reject);
} else {
resolve(self.value);
}
} catch (e){
reject(e)
}
})
});
self.rejectQuene.push(function(){
setTimeout(function(){
try {
if (typeof onRejected === 'function') {
var innerReason = onRejected(self.reason);
Promise.assistPromise4resolve(nextPromise,innerReason,resolve,reject);
} else {
reject(self.reason);
}
} catch (e) {
reject(e);
}
});
});
}
});
return nextPromise;
}
module.exports = Promise;
如何验证这个promise的准确性呢,这里有一个cli工具,定义了800多个测试用例,可以下载下来测试。
npm install promises-aplus-tests -D
注意为了测试这个promise的实现,需要加上这么一段代码供这个工具调用
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
接下来运行一下这个测试工具。
npx promises-aplus-tests ./promise.js
可以看到下面的结果,是可以通过所有测试用例的。