天天看點

看了這個,不能手撕promise你捶我!“1”基礎,自定義promise,你心動了嗎?

“1”基礎,自定義promise,你心動了嗎?

不論是面向就業還是正在學習前端的小夥伴,promise絕對是繞不過的知識點

正所謂學習一個技能的最好辦法是自己創造一個一樣的技能,甚至超越(瞎編的,但确實是這樣)

我們在學習promise的時候,應該需要注意很多問題,如果不了解這些,那promise整個運作過程,我敢說你絕對不會清晰,但是……但是……,看完我這篇文章,讓你明明白白入坑

promise

^ - ^

1、promise你了解多少?

  1. 抽象表達:
    期約是對尚不存在結果的一個替身,而Promise 是一門新的技術(ES6 規範),是 JS 中進行異步程式設計的新解決方案
  2. 具體表達:
    1. 從文法上來說: Promise 是一個構造函數
    1. 從功能上來說: Promise 對象用來封裝一個異步操作并可以擷取其 成功 / 失敗 的結果值

2、promise的狀态

初始狀态為pending,表示正在進行或者即将完成(不确定的狀态)

  1. pending 變為 fulfilled(期約已完成,成功)
  1. pending 變為 rejected(期約已完成,失敗)
隻有這 2 種狀态改變過程, 且一個 promise 對象隻能改變一次,後續不能更改一個已經完成的promise狀态,無論變為成功還是失敗, 都會有一個結果資料

3、使用promise有什麼好處?

  1. 指定回調函數的方式更靈活
    之前是必須在啟動異步之前指定,而promise是:啟動異步任務——>傳回promise對象——>給promise綁定回調函數,而且可以在異步任務結束後指定多個
  2. 支援鍊式調用,可以解決回調地獄問題
    回調地獄是什麼?——回調函數嵌套調用,外部回調函數異步執行的結果是嵌套的回調函數執行的條件。回調地獄會帶來什麼問題?——如果複雜的嵌套回調,代碼會無限想右延伸,代碼風格十分不友好,不便于閱讀,其次,多層嵌套不便于維護

下面實際看一下回調地獄和如何解決回調地獄

//回調地獄
func1(function(result) {
	func2(result, function(newResult) {
		func3(newResult, function(finalResult) {
			console.log('Got the final result: ' + finalResult)
				func4············
		}, failureCallback)
	}, failureCallback)
}, failureCallback)
           
//使用promise解決回調地獄
func1().then(function(result) {
	return doSomethingElse(result)
}).then(function(newResult) {
	return doThirdThing(newResult)
}).then(function(finalResult) {
	console.log('Got the final result: ' + finalResult)
}).catch(failureCallback)
           

4、怎麼使用 promise ?

  1. Promise構造函數:Promise(executor){ }
    其中executor是執行器,在promise内部立即同步調用,異步操作在執行器中執行
  2. Promise.prototype.then( function one ( ) { } , function two ( ) { } )方法:
    成功則執行第一個函數,失敗則執行第二個函數,無論執行哪個函數,都傳回一個新的promise對象(自定義promise中的關鍵問題)
  3. Promise.prototype.catch( function one ( ) { } )方法
    then方法的文法糖,相當于then( undefined , function two ( ) { } )
  4. Promise.resolve ( ) 方法
    一般用于生成一個成功狀态的promise對象
  5. Promise.reject ( ) 方法
    傳回一個失敗狀态的promise對象
  6. Promise.all( [ p1, p2, p3 , ··] )方法
    數組内的元素都是promise對象,該方法傳回一個新的promise對象,隻有數組中的所有的promise都變成成功狀态,新的promise的狀态才是成功
  7. Promise.race( [ p1, p2, p3 , ··] ) 方法
    數組内的元素都是promise對象,該方法傳回一個新的promise對象,數組中隻要有一個promise的狀态為成功,則新的promise則是成功,并且最終的新的promise的狀态和結果由數組中第一個變為成功的promise對象決定
  8. Promise.prototype.done( ) 方法
    Promise 對象的回調鍊,不管以then方法或catch方法結尾,要是最後一個方法抛出錯誤,都有可能無法捕捉到(因為 Promise 内部的錯誤不會冒泡到全局)。是以,es6提供一個done方法,總是處于回調鍊的尾端,保證抛出任何可能出現的錯誤
  9. Promise.prototype.finally( ) 方法
    用于指定不管 Promise 對象最後狀态如何,都會執行的操作。它與done方法的最大差別,它接受一個普通的回調函數作為參數,該函數不管怎樣都必須執行。

5、promise的幾個關鍵問題

  1. 怎麼改變promise的狀态?
    調用resolve函數,pending——>fulfilled,調用reject函數,pending——>rejected,抛出異常,pending——>rejected
  2. 一個promise指定多個成功或者失敗的回調,都會調用嗎?
    隻要當promise變成相應狀态時就會調用
  3. 改變promise狀态和指定回調函數誰先誰後?
    (1) 都有可能, 正常情況下是先指定回調再改變狀态, 但也可以先改狀态再指定回調

    (2) 如何先改狀态再指定回調?

    ① 在執行器中直接調用 resolve()/reject()

    ② 延遲更長時間才調用 then()

    (3) 什麼時候才能得到資料?

    ① 如果先指定的回調, 那當狀态發生改變時, 回調函數就會調用, 得到資料

    ② 如果先改變的狀态, 那當指定回調時, 回調函數就會調用, 得到資料

  4. promise.then()傳回的新 promise 的結果狀态由什麼決定?
    (1) 簡單表達: 由 then()指定的回調函數執行的結果決定

    (2) 詳細表達:

    ① 如果抛出異常, 新 promise 變為 rejected, reason 為抛出的異常

    ② 如果傳回的是非promise的任意值, 新promise變為resolved, value為傳回的值

    ③ 如果傳回的是另一個新promise, 此promise的結果就會成為新promise的結果

  5. promise 如何串連多個操作任務?
    (1) promise 的 then()傳回一個新的 promise, 可以開成 then()的鍊式調用
    (2) 通過 then 的鍊式調用串連多個同步/異步任務
  6. promise 異常傳透是什麼,怎麼解決?
    (1) 當使用 promise 的 then 鍊式調用時, 可以在最後指定失敗的回調,
    (2) 前面任何操作出了異常, 都會傳到最後失敗的回調中處理
  7. 如何中斷 promise 鍊?
    (1) 當使用 promise 的 then 鍊式調用時, 在中間中斷, 不再調用後面的回調函數
    (2) 辦法: 在回調函數中傳回一個 pendding 狀态的 promise 對象

到了激動人心的時刻了,怎麼自定義一個promise呢?

根據上面提出的問題(需要注意的地方),我們就能将自己的promise封裝的完整一些了

我們一起來淦吧!

class MYPromise {//我的類名,區分Promise
	//構造函數
    constructor(executor) {
        this.myPromiseState = "pending";
        this.myPromiseResult = null;
        //當狀态時pending時儲存執行成功和失敗的回調函數,等待狀态改變
        this.callbacks = [];//鍊式調用時,需要儲存回調函數
        const _this = this;

        function resolve(data) {
            if (_this.myPromiseState !== "pending") return;//對應狀态一旦改變,promise的狀态就不能改變
            _this.myPromiseState = "fulfilled";
            _this.myPromiseResult = data;
            //内部異步改變狀态時調用
            setTimeout(() => {
                _this.callbacks.forEach(item => {
                    item.onResolved(data);
                })
            }, 0);
        }

        function reject(data) {
            if (_this.myPromiseState !== "pending") return;
            _this.myPromiseState = "rejected";
            _this.myPromiseResult = data;
            //内部異步改變狀态時調用
            setTimeout(() => {
                _this.callbacks.forEach(item => {
                    item.onRejected(data);
                })
            }, 0);
        }
        try {//抛出異常,調用reject,并且狀态變成失敗
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }

    // then 方法
    then(onResolved, onRejected) {
        const _this = this;
        // 判斷參數,處理catch異常穿透功能
        if (typeof onRejected !== 'function') {
            onRejected = reason => {
                throw reason;
            }
        }
        if (typeof onResolved !== 'function') {
            onResolved = value => value
        }
        return new MYPromise((resolve, reject) => {
            // 封裝try catch函數
            function callback(type) {
                try {
                    let result = type(_this.myPromiseResult);
                    // 判斷是不是promise對象
                    if (result instanceof MYPromise) {
                        //如果是一個promise對象
                        result.then(v => {
                            resolve(v);
                        }, r => {
                            reject(r);
                        })
                    } else {
                        resolve(result);
                    }
                } catch (e) {
                    reject(e);
                }
            }
            if (this.myPromiseState === "fulfilled") {
                setTimeout(() => {
                    callback(onResolved);
                }, 0);
            }
            if (this.myPromiseState === "rejected") {
                setTimeout(() => {
                    callback(onRejected);
                }, 0);
            }
            if (this.myPromiseState === "pending") {
                this.callbacks.push({
                    onResolved: function() {
                        callback(onResolved);
                    },
                    onRejected: function() {
                        callback(onRejected);
                    }
                });
            }
        })
    }

    // catch方法
    catch (onRejected) {
        return this.then(undefined, onRejected);
    }

    // resolve 方法
    static resolve(value) {
        return new MYPromise((resolve, reject) => {
            if (value instanceof MYPromise) {
                value.then(v => {
                    resolve(v);
                }, r => {
                    reject(r);
                })
            } else {
                resolve(value);
            }
        })
    }

    // reject 方法
    static reject(reason) {
        return new MYPromise((resolve, reject) => {
            reject(reason);
        })
    }

    // all 方法
    static all(promises) {
        return new MYPromise((resolve, reject) => {
            // 維護一個變量,表示成功的個數
            let count = 0;
            // 維護一個數組,存放成功的promise的值
            let arr = [];
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    count++;
                    arr[i] = v;
                    if (count === promises.length) {//全部成功,新promise狀态才會成功
                        resolve(arr);
                    }
                }, r => {
                    reject(r);
                })
            }
        })
    }

    // race 方法
    static race(promises) {
        return new MYPromise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(v => {
                    resolve(v);//有一個成功,則新promise狀态為成功,且由數組中第一個成功的promise決定
                }, r => {
                    reject(r);
                })
            }
        })
    }

}
           
//使用我自定義的promise
let p1 = new MYPromise((a, b) => {//使用a,b是為了說明這裡的函數名不是決定性因素,不要被迷惑,真正起作用的是構造函數裡面的resolve和reject函數
    console.log(1111)
    // a("ok")
    // b('ok');
    setTimeout(() => {
        // a("ok")
        b("err")
    }, 0);

});
p1.then(v => {
    console.log(v);
}, r => {
    console.warn(r);
})
           

好了,淦完了,我相信大家一定都會了😜😜😜

最後一句,祝大家天天開心!

繼續閱讀