手動實現一個Promise,簡潔明瞭

JS版本中,更新最大的一個版本就是ES6了,包括了const、let、模板字符串、箭頭函數、類、Generator、Promise等等一系列語法糖。

在ES7沒出來前,Promise是代替傳統回調的主要方式,ES7後更多的是使用async-await,但Pomise的使用仍不在少數

本文章,我們一起實現一個Promise,你可能說,我直接用它就行了,幹嘛自己寫一個。非也,在我看來,理解代碼思想纔是核心,這能大大提高我們的js書寫功底。

下面我們進入正題:

傳參:用then傳入成功或失敗時執行的回調,用catch傳入異常時執行的回調。

返回:都返回promise實例,以實現鏈式調用。

編寫代碼:

class MyPromise 

	constructor(fn) {
		this.doneList = [];
		this.failList = [];
		this.catchCb = function() {};
		setTimeout(() => {
			try {
				fn(this.resolve.bind(this), this.reject.bind(this));
			} catch (e) {
				this.catchCb(e);
			}
		}, 0)
	}
	// 將 .then裏的第一個參數加入到成功隊列
	done(success) {
		if(typeof success === "function") {
			this.doneList.push(success )
		} else {
			throw new Error("傳入success的參數必須爲function!");
		}
		return this;
	}
	// 將 .then裏的第二個參數加入到失敗隊列
	fail(fail) {
		if(typeof fail === "function") {
			this.failList.push(fail )
		} else {
			throw new Error("傳入fail的參數爲必須爲function!");
		}
		return this;
	}
	// 成功時,遍歷成功隊列,執行裏面的所有函數
	resolve(res) {
		this.doneList.forEach(fn => {
			fn(res);
			this.doneList.shift();
		})
	}
	// 成功時,遍歷失敗隊列,執行裏面的所有函數
	reject(err) {
		this.failList.forEach(fn => {
			fn(err);
			this.failList.shift();
		})
	}
	// 將 .then裏的第一、二個參數分別加入到成功隊列、失敗隊列
	then(success, fail) {
		this.done(success || function() {});
		this.fail(fail || function() {});
		return this;
	}
	// 程序出現異常時執行的函數
	catch(errFn) {
		if(typeof errFn !== "function") throw new Error("catch傳入的參數必須爲function");
		this.catchCb = errFn;
	}
	
}

代碼解釋:

Promise的核心是“發佈-訂閱”模式,因此需要一個doneList屬性和一個failList屬性,用來存儲所有訂閱者。

catchCb 屬性用來存儲產生異常時執行的回調函數。

done和fail負責將單個訂閱放進doneList或failList。

catch負責放進異常時的回調函數。

then和done、fail一樣,不同在於接受兩個參數,分別放進doneList 和 failList。

resolve實現發佈,此時遍歷doneList,執行其中所有訂閱者。

reject實現發佈,此時遍歷failList,執行其中所有訂閱者。

constructor裏爲什麼要使用setTimeout爲0s?答:目的是將其裏面的代碼放入等待隊列,待所有回調函數全部轉入Promise容器時,才執行等待隊列的代碼。

下面進入測試:

new MyPromise((resolve, reject) => {
    setTimeout(() => {
        // noExistFn(); // 拋出異常,觸發執行 ①
        // resolve("成功了!"); // 觸發執行 ②
        reject("失敗了!"); // 觸發執行 ③
    }, 1000);
}).then((res) => {
    console.log("res >>> ", res) ①
}, (err) => {
    console.log("err >> ", err) ②
}).catch((exception) => {
    console.log("異常 >> ", exception); ③
});

結果如上,執行noExistFn時,會相應觸發①,以此類推

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章