前端面試必問經典Promise的理解與總結

Promise做爲面試中的經典考題,咱們必定要深入學習和理解它! Promise有什麼用呢?答:咱們拿它解決異步回調問題。面試

概念

異步回調的一個很大的問題在於callback hell也就是「回調地獄」。多層嵌套回調函數,嚴重影響代碼規範。Promise其實是把回調函數從doSomething函數中提取到了後面的then()方法裏,從而防止多重嵌套。一個Promise對象表示目前還不可用可是將來某個節點能夠被解析的值,這個值要麼被解析成功,要麼失敗拋出異常。它容許咱們以同步的方式編寫異步代碼。數組

使用方法

Promise的構造函數用來構造一個Promise對象,其中入參匿名函數中resolve和reject這兩個也都是函數。若是resolve執行了,則出發Promise.then中成功的回調函數,若是reject執行了,則觸發了promise.then中拒絕的回調函數。promise

一個Promise對象一開始的值是pending準備狀態,執行了resolve()後,Promise對象的狀態值變爲onFulfilled,執行了reject()後,狀態值變爲onRejected。Promise對象的狀態值一旦肯定,就不會再改變。bash

異常捕獲

promise有兩種異常捕獲方式,一個是then中的reject,另外一個是catch()方法。異步

then中的reject方法捕獲異常

沒法捕獲當前then中拋出的異常函數

var promise = Promise.resolve();
promise.then(()=>{
    throw new Error("BOOM!");
}).then((success)=>{
    console.log(success);
}, (error)=>{
    console.log(error);
});
複製代碼

catch捕獲異常

catch不只能捕獲then中拋出的異常,還能捕獲前面promise拋出的異常,因此建議使用catch方法。學習

var promise = Promise.reject("Boom!");
promise.then(()=>{
    return "success";
}).then((success) => {
    console.log(success);
    throw new Error("Another Boom!");
}).catch((error) => {
    console.log(error); //BOOM!
});
複製代碼

手寫Promise

基礎篇-小試牛刀

首先來看基礎版的代碼,能夠實現簡單的同步代碼,這一步是必需要可以寫出來的。ui

// 首先要明確Promise是一個類,因此咱們用class聲明。
// 其次,構造函數中接收一個executor,它有兩個參數,一個是resolve,一個是reject
// 這裏要注意,resolve和reject都是函數
class Promise(){
    // 構造函數(入參是執行器,包括resolve和reject兩個函數)
    constructor(executor){
        // 必要的初始化,這裏用到狀態,值和緣由三個變量
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // 定義成功函數,入參是value
        let resolve = value => {
            // 首先要判斷state是否爲等待態,若是不是則不作任何處理
            if(this.state === 'pending'){
                // 修改狀態
                this.state = 'fulfilled';
                // 更新值
                this.value = value;
            }
        };
        // 定義失敗函數,入參是失敗緣由
        let reject = reason => {
            // 一樣的邏輯
            if(this.state === 'pending'){
                this.state = 'rejected';
                this.reason = reason;
            }
        };
        // 這是promise對象的的主邏輯,執行executor,若是執行器出錯,則捕獲錯誤後執行reject函數
        try{
            executor(resolve, reject); 
        }catch(err){
            reject(err);
        }
    }
    // 定義Promise的then函數
    // then方法接收兩個參數,若是狀態爲fulfilled,執行onFulfilled
    // 若是狀態爲rejected,則執行onRejected
    then(onFulfilled, onRejected){
        if(this.state === 'fulfilled'){
            onFulfilled(this.value);
        };
        if(this.state === 'rejected'){
            onRejected(this.reason);
        };
    }
}
複製代碼

進階篇 解決異步實現

class Promise{
    constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        // 成功回調函數數組和失敗回調函數數組
        this.onResolveCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = value => {
            if(this.state === 'pending'){
                this.state = 'fulfilled';
                this.value = value;
                // 成功的話遍歷成功回調函數數組而後執行這些函數
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        };
        let reject = reason => {
            if(this.state === 'pending'){
                this.state = 'rejected';
                this.reason = reason;
                // 失敗的話遍歷失敗回調函數數組而後執行這些函數
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        };
        try{
            executor(resolve,reject)
        }catch(err){
            reject(err);
        }
        then(onFulfilled, onRejected){
            if(this.state === 'fulfilled'){
                onFulfilled(this.value);
            }
            if(this.state === 'rejected'){
                onRejected(this.reason);
            }
            // 當狀態爲等待態時,咱們要將成功/失敗的回調函數加入到對應的數組中
            if(this.state === 'pending'){
                // onFulfilled傳入到成功數組
                this.onResolvedCallbacks.push(()=>{
                    onFulfilled(this.value);
                })
                // onRejected傳入到成功數組
                this.onRejectedCallbacks.push(()=>{
                    onRejeced(this.reason);
                })
            }
        }
    }
}
複製代碼

威力增強版 解決鏈式調用

class Promise{
    constructor(executor){
        this.state = 'pending';
        this.value = undefined;
        this.reason = undefined;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = value => {
            if(this.state === 'pending'){
                this.state = 'fulfilled';
                this.value = value;
                this.onResolvedCallbacks.forEach(fn=>fn());
            }
        };
        let reject = reason => {
            if(this.state === 'pending'){
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn=>fn());
            }
        };
        try{
            executor(resolve,reject);
        }catch(err){
            reject(err);
        }
    }
    then(onFulfilled, onRejected){
        let promise2 = new Promise((resolve,reject) => {
            if(this.state === 'fulfilled'){
               let x = onFulfilled(this.value); 
               resolvePromise(promise2, x, resolve, reject);
            };
            if(this.state === 'rejected'){
                let x = onRejected(this.reason);
                resolvePromise(promise2, x, resolve, reject);
            };
            if(this.state === 'pending'){
                this.onResolvedCallbacks.push(()=> {
                    let x = onFulfilled(this.value);
                    resolvePromise(promise2, x, resolve, reject);
                })
                this.onRejectedCallbacks.push(()=>{
                    let x = onRejected(this.reason);
                    resolvePromise(promise2, x, resolve, reject);
                })
            };
        });
        return promise2;
    }
    function resolvePromise(promise2, x, resolve, reject){
        if(x === promise2){
            return reject(new TypeError('Chaining cycle detected for promise');
        }
        let called;
        if(x != null && (typeof x === 'object' || typeof x === 'function')){
            try{
                let then = x.then;
                if(typeof then === 'function'){
                    then.call(x, y=>{
                        if(called){
                            return;
                        }
                        called = true;
                        resolvePromise(promise2,y,resolve,reject);
                    }, err => {
                        if(called) return;
                        called = true;
                        reject(err);
                    })
                }else{
                    resolve(x);
                }
            }catch(e){
                if(called) return;
                called = true;
                reject(e);
            }
        }else{
            resolve(x);
        }
    }
}
複製代碼
相關文章
相關標籤/搜索