前言
異步編程作爲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
可以看到下面的結果,是可以通過所有測試用例的。