從Promise基本用法,實現手寫Promise

Promise

Promise是異步編程的⼀種解決⽅案。編程

Promise對象有如下兩個特色:數組

  1. 對象的狀態不受外界影響。
    Promise對象表明一個異步操做,有3種狀態:Pending、Fulfilled和Rejected。只有異步操做的結果能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。
  2. ⼀旦狀態改變就不會再變,任什麼時候候均可以獲得這個結果。
    Promise對象的狀態改變只有兩種可能:從Pending變爲Fulfilled和從Pending變爲Rejected。只要這兩種狀況發⽣,狀態就凝固了,不會再 變,⽽是⼀直保持這個結果,這時就稱爲Resolved(已定型)。

基本用法

// 建立一個promise對象
var promise = new Promise(function(resolve, reject) {
    ... 
    if(/*異步操做成功*/){
        resolve(data);
    }else {
        reject(err)
    }
)
複製代碼

Promise構造函數接受⼀個函數做爲參數,該函數的兩個參數分別是resolve和reject。它們是兩個函數,由JavaScript引擎提供,不⽤⾃⼰部署。promise

  • resolve函數的做⽤是:Pending-->Resolved,成功時調⽤,並將異步操做的結果做爲參數傳遞出去;
  • reject函數的做⽤是,Pending-->Rejected,失敗時調⽤,並將異步操做報出的錯誤做爲參數傳遞出去。

Promise實例⽣成之後,能夠⽤then⽅法分別指定Resolved狀態和Rejected狀態的回調函數。bash

promise.then(function(value)  {
    // success
}, function(err){
    // failure
})
複製代碼

then ⽅法能夠接受兩個回調函數做爲參數。成功回調和失敗回調,其中,第⼆個函數是可選的,不⼀定要提供。這兩個函數都接受Promise對象傳出的值做爲參數。異步

須要注意的是:異步編程

  • Promise新建後會當即執行,
  • then方法指定的回調函數將在一步操做後執行

手寫Promise:

從Promise的使用來看能夠分爲兩部分:新建實例和調用實例的then方法。 因此咱們也能夠針對這兩部分分別實現:函數

  • 實現Promise構造函數
  • 實現then方法
實現Promise構造函數

首先從構造函數出發,根據上文對Promise的描述,咱們來實現這個Promise構造函數。ui

Promise構造函數接受⼀個函數做爲參數,該函數的兩個參數分別是resolve和reject。他們的做用是將state從Pending變爲fulfilled或者rejectedthis

function Promise (executor) {
    this.state = 'pending';
    this.data = undefined;
    this.reason= undefined;
    this.fn1Callbacks=[];
    this.fn2Callbacks=[];
    let resolve = value => {
        if(this.state === 'pending'){
            this.state = 'fulfilled';
            this.data = value;
            for(let i = 0; i<this.fn1Callbacks.length; i++){
                self.fn1Callbacks[i](value);
            }
        }
    };
    let reject = reason => {
        if(this.state === 'pending'){
            this.state = 'rejected';
            this.reason = reason;
            for (let i = 0; i < self.fn2Callback.length; i++) {
                self.fn2Callback[i](reason);
            }
        }
    };
    try {
        executor(resolve, reject);
    } catch (err) {
        reject(err);
    }
}
複製代碼
實現 then 方法

當Promise的狀態發生了改變,不管是成功或是失敗都會調用then方法。spa

Promise實例具備then方法,即then方法是定義在原型對象Promise.prototype上的。它的做用是爲Promise實例添加狀態改變時的回調函數並在異步完成時執行這個函數。

then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。所以能夠採用鏈式寫法,即then方法後面再調用另外一個then方法。

基於上面的討論咱們很容易得出結論:

  • then方法能夠在實例上調用。所以then 方法的實現是在Promise的 prototype上。
  • 他的做用是爲Promise實例添加狀態改變時的回調函數並在異步完成時執行這個函數
  • then方法會返回一個Promise,並且是返回一個新的Promise(詳情)對象。

下面咱們來看看promise實現

// then方法接收兩個參數,fn1,fn2,分別爲Promise成功或失敗後的回調
Promise.prototype.then = function(fn1, fn2) {
  var self = this
  var promise2

  // 首先對入參 fn1, fn2作判斷
  fn1 = typeof fn1 === 'function' ? fn1 : function(v) {}
  fn2 = typeof fn2 === 'function' ? fn2 : function(r) {}

  if (self.status === 'resolved') {
    return promise2 = new Promise(function(resolve, reject) {
        //todo
    })
  }

  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
       //todo
    })
  }

  if (self.status === 'pending') {
    return promise2 = new Promise(function(resolve, reject) {
       // todo
    })
  }
}
複製代碼

首先是對輸入的成功回調和失敗回調進行類型判斷,明確是個函數再執行它,接下來是處理Promise中可能存在的有三種可能的狀態,咱們分三個if塊來處理,在裏面分別都返回一個new Promise。

因此,接下來的邏輯是:

  • 若是 promise 狀態是 resolved,須要執行 fn1 ;
  • 若是 promise 狀態是 rejected, 須要執行fn2 ;
  • 若是 promise 狀態是 pending, 咱們並不能肯定調用 fn1 仍是 fn2 ,只能先把方法都保存在 fn1Callback, fn2Callback 數組中。等到Promise的狀態肯定後再處理。

根據上面的邏輯,填充下面代碼:

Promise.prototype.then = function(fn1, fn2) {
    var self = this
    var promise2
    fn1 = typeof fn1 === 'function' ? fn1 : function(v) {}
    fn2 = typeof fn2 === 'function' ? fn2 : function(r) {}
    if (self.status === 'resolved') {
        return promise2 = new Promise(function(resolve, reject) {
            // 把 fn一、fn2 放在 try catch 裏面,畢竟 fn一、fn2 是用戶傳入的,報錯嘛,很常見
            try {
                var x = fn1(self.data)
                // fn1 執行後,會有返回值,經過 resolve 注入到 then 返回的 promise 中
                resolve(x)
            } catch (e) {
                reject(e)                
            }
        })
    }
    if (self.status === 'rejected') {
        return promise2 = new Promise(function(resolve, reject) {
            try {
                var x = fn2(self.data)
                reject(x)
            } catch (e) {
                reject(e)
            }
        })
    }
    if (self.status === 'pending') {
        return promise2 = new Promise(function(resolve, reject) {
            this.fn1Callback.push(function(value){
                try {
                    var x = fn1(self.data);
                    resolve(x)
                } catch (e) {
                    reject(e)
                }
            })
            this.fn2Callback.push(function(value) {
                try {
                    var x = fn2(self.data);
                    reject(x)
                } catch (e) {
                    reject(e)
                }
            })
        })
    }
}
複製代碼
相關文章
相關標籤/搜索