深刻理解、實現promise、async/await

promise、async/await的提出,已由來已久,不少人都知道這兩個都是爲了解決回調地獄問題的,面試中不少面試官都喜歡那這兩個做比較,那我今天就來講說promise、async/await的區別以及簡單的原理實現。避免面試的尷尬!

篇幅比較長,理解和實現兩部分(主要在實現),最近看到不少面試的同窗都有提到手寫promise、async/await的問題,就隨手整理了一下,以前也整理了一下嘮一嘮call、apply和bind以及手動實現(拒絕晦澀難懂)git

promise

Promise,簡單來講就是一個容器,裏面保存着某個將來纔會結束的時間(一般是一個異步操做的結果),經過鏈式調用同步實現異步;github

特色面試

  • 對象的狀態不受外界影響;
  • 鏈式調用(依舊沒逃出回調的問題)
  • 異常處理(catch
  • 狀態一旦改變,就不會再變,狀態凝固;

具體的能夠關注下關於Promise以及你可能不知道的6件事以及聊一聊Promise的坑npm

Promise/A+實現

這篇剖析Promise內部結構寫的很不錯,這裏也分享給你們,面試當中寫個很簡單的就能夠了,對於源碼咱們理解便可;編程

實現Promise的要求:segmentfault

  • 構造一個Promise實例須要給Promise構造函數傳入一個函數,傳入的函數須要有兩個形參,即resolvereject注意兩個形參都是function類型的參數;
  • Promise上還有then方法(多個then可造成鏈式調用),then方法就是用來指定Promsie對象的狀態改變肯定執行的操做,resolve時執行第一個函數(onFulfilled), reject時執行第二個函數(onRejected);
  • 當狀態變爲resolve後就不能在改變成reject, 反之同理;

基於以上要求咱們能夠實現一個簡單的Promise:promise

// promise 三個狀態
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class myPromise {
    constructor(executor) {
        this.status = PENDING; // 聲明初始狀態;
        this.value = undefined; // fulfilled狀態時 返回的信息
        this.reason = undefined; // rejected狀態時 拒絕的緣由;
        this.onFulfilledCallbacks = []; // 存儲fulfilled狀態對應的onFulfilled函數
        this.onRejectedCallbacks = []; // 存儲rejected狀態對應的onRejected函數
        //=>執行Excutor
        let resolve = result => { // resolve方法
            if (this.status !== PENDING) return;

            // 爲何resolve 加setTimeout?
            // 2.2.4規範 onFulfilled 和 onRejected 只容許在 execution context 棧僅包含平臺代碼時運行.
            // 這裏的平臺代碼指的是引擎、環境以及promise的實施代碼。實踐中要確保onFulfilled 和 onRejected方法異步執行,且應該在then方法被調用的那一輪事件循環以後的新執行棧中執行。

            setTimeout(() => {
                //只能由pending狀態=>fulfilled狀態(避免調用屢次resolve reject)
                this.status = FULFILLED;
                this.value = result;
                this.onFulfilledCallbacks.forEach(cb => cb(this.value));
            }, 0);
        };
        let reject = reason => { // reject方法
            if (this.status !== PENDING) return;
            setTimeout(() => {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(cb => cb(this.reason))
            })
        };
        // 捕獲在executor執行器中拋出的異常
        try {
            executor(resolveFn, rejectFn);
        }
        catch (err) {
            //=>有異常信息按照rejected狀態處理
            reject(err);
        }
    }

    // 添加.then方法
    then(onFullfilled, onRejected) {
        this.onFulfilledCallbacks.push(onFullfilled);
        this.onRejectedCallbacks.push(onRejected);
    }

    // 添加.catch方法
    catch(onRejected) {
        return this.then(null, onRejected);
    }
}

module.exports = myPromise;

不過在面試中有些會要求手寫一個符合Promises/A+規範的完美Promise。app

首先來看下Promises規範:異步

Promise 規範有不少,如Promise/A,Promise/B,Promise/D 以及 Promise/A 的升級版 Promise/A+。ES6 中採用了Promise/A+ 規範。async

// promise 三個狀態
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class myPromise {
    constructor(executor) {
        this.status = PENDING; // 聲明初始狀態;
        this.value = undefined; // fulfilled狀態時 返回的信息
        this.reason = undefined; // rejected狀態時 拒絕的緣由;
        this.onFulfilledCallbacks = []; // 存儲fulfilled狀態對應的onFulfilled函數
        this.onRejectedCallbacks = []; // 存儲rejected狀態對應的onRejected函數
        //=>執行Excutor
        let resolve = result => { // resolve方法
            if (this.status !== PENDING) return;

            // 爲何resolve 加setTimeout?
            // 2.2.4規範 onFulfilled 和 onRejected 只容許在 execution context 棧僅包含平臺代碼時運行.
            // 這裏的平臺代碼指的是引擎、環境以及promise的實施代碼。實踐中要確保onFulfilled 和 onRejected方法異步執行,且應該在then方法被調用的那一輪事件循環以後的新執行棧中執行。

            setTimeout(() => {
                //只能由pending狀態=>fulfilled狀態(避免調用屢次resolve reject)
                this.status = FULFILLED;
                this.value = result;
                this.onFulfilledCallbacks.forEach(cb => cb(this.value));
            }, 0);
        };
        let reject = reason => { // reject方法
            if (this.status !== PENDING) return;
            setTimeout(() => {
                this.status = REJECTED;
                this.reason = reason;
                this.onRejectedCallbacks.forEach(cb => cb(this.reason))
            })
        };
        // 捕獲在executor執行器中拋出的異常
        try {
            executor(resolveFn, rejectFn);
        }
        catch (err) {
            //=>有異常信息按照rejected狀態處理
            reject(err);
        }
    }

    // 添加.then方法
    then(onFullfilled, onRejected) {
        onFullfilled = typeof onFullfilled === "function" ? onFullfilled : value => value;
        onRejected = typeof onRejected === "function" ? onRejected : reason => {
            throw reason;
        };
        switch (self.status) {
            case PENDING:
                // 當異步調用resolve/rejected時 將onFulfilled/onRejected收集暫存到集合中
                return promise2 = new myPromise((resolve, reject) => {
                    this.onFulfilledCallbacks.push(()=>{
                        try {
                            let x = onFullfilled(this.value);
                            this.resolvePromise(promise2, x, resolve, reject); // 新的promise resolve 上一個onFulfilled的返回值
                        }
                        catch (e) {
                            reject(e); // 捕獲前面onFulfilled中拋出的異常 then(onFulfilled, onRejected);
                        }
                    });
                    this.onRejectedCallbacks.push(() => {
                        try {
                            let x = onRejected(this.reason);
                            this.resolvePromise(promise2, x, resolve, reject);
                        }
                        catch (e) {
                            reject(e); // error catch
                        }
                    });
                });
                break;
            case FULFILLED:
                return promise2 = new myPromise(function (resolve, reject) {
                    try {
                        let x = onFullfilled(this.value);
                        //將上次一then裏面的方法傳遞進下一個Promise狀態
                        this.resolvePromise(promise2, x, resolve, reject);
                    }
                    catch (e) {
                        reject(e);//error catch
                    }
                });
                break;
            case REJECTED:
                return promise2 = new myPromise(function (resolve, reject) {
                    try {
                        let x = onRejected(this.reason);
                        //將then裏面的方法傳遞到下一個Promise的狀態裏
                        this.resolvePromise(promise2, x, resolve, reject);
                    }
                    catch (e) {
                        reject(e);
                    }
                });
                break;
            default:
        }
    }

    // 添加.catch方法
    catch(onRejected) {
        return this.then(null, onRejected);
    }

    static deferred() { // 延遲對象
        let defer = {};
        defer.promise = new myPromise((resolve, reject) => {
            defer.resolve = resolve;
            defer.reject = reject;
        });
        return defer;
    }

    static all(promises = []) {
        let index = 0,
            result = [];
        return new Promise((resolve, reject) => {
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(val => {
                    index++;
                    result[i] = val;
                    if (index === promises.length) {
                        resolve(result)
                    }
                }, reject);
            }
        })
    }
    
    static resolvePromise(promise, x, resolve, reject) {
        if (promise === x) { // 若是從onFulfilled中返回的x 就是promise2 就會致使循環引用報錯
            throw new TypeError("type error")
        }
        let isUsed;
        if (x !== null && (x instanceof Object || x instanceof Function)) {
            try {
                let then = x.then;
                if (typeof then === "function") {
                    //是一個promise的狀況
                    then.call(x,(y) => {
                        if (isUsed) return;
                        isUsed = true;
                        this.resolvePromise(promise, y, resolve, reject);
                    },(e) => {
                        if (isUsed) return;
                        isUsed = true;
                        reject(e);
                    })
                }
                else {
                    //僅僅是一個函數或者是對象
                    resolve(x)
                }
            }
            catch (e) {
                if (isUsed) return;
                isUsed = true;
                reject(e);
            }
        }
        else {
            //返回的基本類型,直接resolve
            resolve(x)
        }
    }
}

module.exports = myPromise;

Promise測試

npm i -g promises-aplus-tests

promises-aplus-tests Promise.js

promise的原理Promise/A+的實現就結束了。


async/await

  • async/await更加語義化,async 是「異步」的簡寫,async function 用於申明一個 function 是異步的; await,能夠認爲是async wait的簡寫, 用於等待一個異步方法執行完成;
  • async/await是一個用同步思惟解決異步問題的方案(等結果出來以後,代碼纔會繼續往下執行)
  • 能夠經過多層 async function的同步寫法代替傳統的callback嵌套,規避掉了Promise的鏈式調用,代碼看起來簡單明瞭;

栗子

function sleep(wait) {
    return new Promise((res,rej) => {
        setTimeout(() => {
            res(wait);
        },wait);
    });
}

async function demo() {
    let result01 = await sleep(100);
    //上一個await執行以後纔會執行下一句
    let result02 = await sleep(result01 + 100);
    let result03 = await sleep(result02 + 100);
    // console.log(result03);
    return result03;
}

demo().then(result => {
    console.log(result);
});
異步編程的最高境界,根本就不用關心它異步,不少人認爲它是異步操做的終極解決方案;可是和 Promise不存在誰取代誰,由於 async/await是寄生於 PromiseGenerator的語法糖。

Generator還比較陌生的同窗能夠看下廖雪峯寫的generator,這裏就不作過多介紹了。

實現一個簡單的async/await

async/await語法糖就是使用Generator函數+自動執行器來運做的。 咱們能夠參考如下例子

// 定義了一個promise,用來模擬異步請求,做用是傳入參數++
function getNum(num){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(num+1)
        }, 1000)
    })
}

//自動執行器,若是一個Generator函數沒有執行完,則遞歸調用
function asyncFun(func){
  var gen = func();

  function next(data){
    var result = gen.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

// 所須要執行的Generator函數,內部的數據在執行完成一步的promise以後,再調用下一步
var func = function* (){
  var f1 = yield getNum(1);
  var f2 = yield getNum(f1);
  console.log(f2) ;
};
asyncFun(func);

在執行的過程當中,判斷一個函數的promise是否完成,若是已經完成,將結果傳入下一個函數,繼續重複此步驟。


結束

若是發現過程遇到什麼問題,歡迎及時提出
相關文章
相關標籤/搜索