從0到1實現Promise

前言

Promise你們必定都不陌生了,JavaScript異步流程從最初的Callback,到Promise,到Generator,再到目前使用最多的Async/Await(若是對於這些不熟悉的能夠參考我另外一篇文章《JavaScript異步編程》),這不只僅是技術實現的發展,更是思想上對於如何控制異步的遞進。Promise做爲後續方案的基礎,是重中之重,也是面試時候最常被問到的。git

今天咱們就一塊兒從0到1實現一個基於A+規範的Promise,過程當中也會對Promise的異常處理,以及是否可手動終止作一些討論,最後會對咱們實現的Promise作單元測試。完整的代碼已經上傳到github,想直接看代碼的能夠點這裏github

雖然已經有不少帶你實現Promise類的文章了,但每一個人理解的程度不同,也許不一樣的文章能夠帶給你不一樣的思考呢,那咱們就開始吧。面試

正文

1. 基礎框架

new Promise()時接收一個executor函數做爲參數,該函數會當即執行,函數中有兩個參數,它們也是函數,分別是resolve和reject,函數同步執行必定要放在try...catch中,不然沒法進行錯誤捕獲。算法

MyPromise.js編程

function MyPromise(executor) {

  function resolve(value) {

  }

  function reject(reason) {
    
  }

  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}

module.exports = MyPromise;

resolve()接收Promise成功值value,reject接收Promise失敗緣由reason。segmentfault

test.js數組

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  resolve(123);
})

2. 添加狀態機

目前實現存在的問題:promise

  1. Promise是一個狀態機的機制,初始狀態爲 pending,成功狀態爲 fulfilled,失敗狀態爲 rejected。只能從 pending -> fulfilled,或者從 pending -> rejected,而且狀態一旦轉變,就永遠不會再變了。

因此,咱們須要爲Promise添加一個狀態流轉的機制。微信

MyPromise.js框架

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;
  self.state = PENDING;


  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
    }
  }

  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}

module.exports = MyPromise;

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  resolve(123);
});

promise.then(function(value) {
  console.log('value', value);
}, function(reason) {
  console.log('reason', reason);
})

3. 添加then方法

Promise擁有一個then方法,接收兩個函數 onFulfilledonRejected,分別做爲Promise成功和失敗的回調。因此,在then方法中咱們須要對狀態state進行判斷,若是是fulfilled,則執行onFulfilled(value)方法,若是是rejected,則執行onRejected(reason)方法。

因爲成功值value和失敗緣由reason是由用戶在executor中經過resolve(value)reject(reason)傳入的,因此咱們須要有一個全局的valuereason供後續方法獲取。

MyPromise.js

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;

  self.state = PENDING;
  self.value = null;
  self.reason = null;

  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
      self.value = value;
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
      self.reason = reason;
    }
  }

  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;

  if (self.state === FULFILLED) {
    onFuifilled(self.value);
  }

  if (self.state === REJECTED) {
    onRejected(self.reason);
  }
};

module.exports = MyPromise;

4. 實現異步調用resolve

目前實現存在的問題:

  1. 同步調用resolve()沒有問題,但若是是異步調用,好比放到setTimeout中,由於目前的代碼在調用then()方法時,state還是pending狀態,當timer到時候調用resolve()state修改成fulfilled狀態,可是onFulfilled()函數已經沒有時機調用了。

針對上述問題,進行以下修改:

MyPromise.js

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function MyPromise(executor) {
  let self = this;

  self.state = PENDING;
  self.value = null;
  self.reason = null;
  self.onFulfilledCallbacks = [];
  self.onRejectedCallbacks = [];

  function resolve(value) {
    if (self.state === PENDING) {
      self.state = FULFILLED;
      self.value = value;

      self.onFulfilledCallbacks.forEach(function(fulfilledCallback) {
        fulfilledCallback();
      });
    }
  }

  function reject(reason) {
    if (self.state === PENDING) {
      self.state = REJECTED;
      self.reason = reason;

      self.onRejectedCallbacks.forEach(function(rejectedCallback) {
        rejectedCallback();
      });
    }
  }

  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;

  if (self.state === PENDING) {
    self.onFulfilledCallbacks.push(() => {
        onFuifilled(self.value);
    });
    self.onRejectedCallbacks.push(() => {
        onRejected(self.reason);
    });
  }

  if (self.state === FULFILLED) {
    onFuifilled(self.value);
  }

  if (self.state === REJECTED) {
    onRejected(self.reason);
  }
};

module.exports = MyPromise;

咱們添加了兩個回調函數數組onFulfilledCallbacksonRejectedCallbacks,用來存儲then()方法中傳入的成功和失敗回調。而後,當用戶調用resolve()reject()的時候,修改state狀態,並從相應的回調數組中依次取出回調函數執行。

同時,經過這種方式咱們也實現了能夠註冊多個then()函數,而且在成功或者失敗時按照註冊順序依次執行。

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  setTimeout(function() {
    resolve(123);
  }, 1000);
});

promise.then(function(value) {
  console.log('value1', value);
}, function(reason) {
  console.log('reason1', reason);
});

promise.then(function(value) {
  console.log('value2', value);
}, function(reason) {
  console.log('reason2', reason);
});

5. then返回的還是Promise

讀過PromiseA+規範的同窗確定知道,then()方法返回的還是一個Promise,而且返回Promise的resolve的值是上一個Promise的onFulfilled()函數或onRejected()函數的返回值。若是在上一個Promise的then()方法回調函數的執行過程當中發生了錯誤,那麼會將其捕獲到,並做爲返回的Promise的onRejected函數的參數傳入。好比:

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value1', value);
  return 456;
}).then((value) => {
  console.log('value2', value);
});

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

打印結果爲:

value1 123
value2 456
let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value1', value);
  a.b = 2;    // 這裏存在語法錯誤
  return 456;
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印結果爲:

value1 123
reason2 ReferenceError: a is not defined

能夠看到,then()方法回調函數若是發生錯誤,會被捕獲到,那麼then()返回的Promise會自動變爲onRejected,執行onRejected()回調函數。

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  console.log('value1', value);
  return 456;
}, (reason) => {
  console.log('reason1', reason);
  return 456;
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印結果爲:

reason1 123
value2 456

好啦,接下來咱們就去實現then()方法依然返回一個Promise。

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;
  let promise2 = null;

  promise2 = new MyPromise((resolve, reject) => {
    if (self.state === PENDING) {
      self.onFulfilledCallbacks.push(() => {
        try {
          let x = onFuifilled(self.value);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      });
      self.onRejectedCallbacks.push(() => {
        try {
          let x = onRejected(self.reason);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch(reason) {
          reject(reason);
        }
      });
    }
  
    if (self.state === FULFILLED) {
      try {
        let x = onFuifilled(self.value);
        self.resolvePromise(promise2, x, resolve, reject);
      } catch (reason) {
        reject(reason);
      }
    }
  
    if (self.state === REJECTED) {
      try {
        let x = onRejected(self.reason);
        self.resolvePromise(promise2, x, resolve, reject);
      } catch (reason) {
        reject(reason);
      }
    }
  });

  return promise2;
};

能夠看到,咱們新增了一個promise2做爲then()方法的返回值。經過let x = onFuifilled(self.value) 或者 let x = onRejected(self.reason)拿到then()方法回調函數的返回值,而後調用self.resolvePromise(promise2, x, resolve, reject),將新增的promise2xpromise2resolvereject傳入到resolvePromise()中。

因此,下面咱們重點看一下resolvePromise()方法。

MyPromise.js

MyPromise.prototype.resolvePromise = function(promise2, x, resolve, reject) {
  let self = this;
  let called = false;   // called 防止屢次調用

  if (promise2 === x) {
    return reject(new TypeError('循環引用'));
  }

  if (x !== null && (Object.prototype.toString.call(x) === '[object Object]' || Object.prototype.toString.call(x) === '[object Function]')) {
    // x是對象或者函數
    try {
      let then = x.then;

      if (typeof then === 'function') {
        then.call(x, (y) => {
          // 別人的Promise的then方法可能設置了getter等,使用called防止屢次調用then方法
          if (called) return ;
          called = true;
          // 成功值y有可能仍是promise或者是具備then方法等,再次resolvePromise,直到成功值爲基本類型或者非thenable
          self.resolvePromise(promise2, y, resolve, reject);
        }, (reason) => {
          if (called) return ;
          called = true;
          reject(reason);
        });
      } else {
        if (called) return ;
        called = true;
        resolve(x);
      }
    } catch (reason) {
      if (called) return ;
      called = true;
      reject(reason);
    }
  } else {
    // x是普通值,直接resolve
    resolve(x);
  }
};

resolvePromise()是用來解析then()回調函數中返回的還是一個Promise,這個Promise有多是咱們本身的,有多是別的庫實現的,也有多是一個具備then()方法的對象,因此這裏靠resolvePromise()來實現統一處理。

下面是翻譯自PromiseA+規範關於resolvePromise()的要求:


Promise 解決過程

Promise 解決過程是一個抽象的操做,其需輸入一個 promise 和一個值,咱們表示爲 [[Resolve]](promise, x),若是 x 有 then 方法且看上去像一個 Promise ,解決程序即嘗試使 promise 接受 x 的狀態;不然其用 x 的值來執行 promise 。

這種 thenable 的特性使得 Promise 的實現更具備通用性:只要其暴露出一個遵循 Promise/A+ 協議的 then 方法便可;這同時也使遵循 Promise/A+ 規範的實現能夠與那些不太規範但可用的實現能良好共存。

運行 [[Resolve]](promise, x) 需遵循如下步驟:

  • x 與 promise 相等

若是 promise 和 x 指向同一對象,以 TypeError 爲據因拒絕執行 promise

  • x 爲 Promise

若是 x 爲 Promise ,則使 promise 接受 x 的狀態:

- 若是 x 處於等待態, promise 需保持爲等待態直至 x 被執行或拒絕
- 若是 x 處於執行態,用相同的值執行 promise
- 若是 x 處於拒絕態,用相同的據因拒絕 promise
  • x 爲對象或函數

若是 x 爲對象或者函數:

- 把 x.then 賦值給 then
- 若是取 x.then 的值時拋出錯誤 e ,則以 e 爲據因拒絕 promise
- 若是 then 是函數,將 x 做爲函數的做用域 this 調用之。傳遞兩個回調函數做爲參數,第一個參數叫作 resolvePromise ,第二個參數叫作 rejectPromise:
    - 若是 resolvePromise 以值 y 爲參數被調用,則運行 [[Resolve]](promise, y)
    - 若是 rejectPromise 以據因 r 爲參數被調用,則以據因 r 拒絕 promise
    - 若是 resolvePromise 和 rejectPromise 均被調用,或者被同一參數調用了屢次,則優先採用首次調用並忽略剩下的調用
    - 若是調用 then 方法拋出了異常 e:
        - 若是 resolvePromise 或 rejectPromise 已經被調用,則忽略之
        - 不然以 e 爲據因拒絕 promise
    - 若是 then 不是函數,以 x 爲參數執行 promise
- 若是 x 不爲對象或者函數,以 x 爲參數執行 promise

若是一個 promise 被一個循環的 thenable 鏈中的對象解決,而 [[Resolve]](promise, thenable) 的遞歸性質又使得其被再次調用,根據上述的算法將會陷入無限遞歸之中。算法雖不強制要求,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識別的 TypeError 爲據因來拒絕 promise。


參考上述規範,結合代碼中的註釋,相信你們能夠理解resolvePromise()的做用了。

測試:

test.js

let MyPromise = require('./MyPromise.js');

let promise = new MyPromise(function(resolve, reject) {
  setTimeout(function() {
    resolve(123);
  }, 1000);
});

promise.then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(456);
  }).then((value) => {
    return new MyPromise((resolve, reject) => {
      resolve(789);
    })
  });
}, (reason) => {
  console.log('reason1', reason);
}).then((value) => {
  console.log('value2', value);
}, (reason) => {
  console.log('reason2', reason);
});

打印結果:

value1 123
value2 789

6. 讓then()方法的回調函數老是異步調用

官方Promise實現的回調函數老是異步調用的:

console.log('start');

let promise = new Promise((resolve, reject) => {
  console.log('step-');
  resolve(123);
});

promise.then((value) => {
  console.log('step--');
  console.log('value', value);
});

console.log('end');

打印結果:

start
step-
end
step--
value1 123

Promise屬於微任務,這裏咱們爲了方便用宏任務setTiemout來代替實現異步,具體關於宏任務、微任務以及Event Loop能夠參考個人另外一篇文章帶你完全弄懂Event Loop

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  let self = this;
  let promise2 = null;

  promise2 = new MyPromise((resolve, reject) => {
    if (self.state === PENDING) {
      self.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFuifilled(self.value);
            self.resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
      });
      self.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(self.reason);
            self.resolvePromise(promise2, x, resolve, reject);
          } catch (reason) {
            reject(reason);
          }
        }, 0);
      });
    }
  
    if (self.state === FULFILLED) {
      setTimeout(() => {
        try {
          let x = onFuifilled(self.value);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }, 0);
    }
  
    if (self.state === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(self.reason);
          self.resolvePromise(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      }, 0);
    }
  });

  return promise2;
};

測試:

test.js

let MyPromise = require('./MyPromise.js');

console.log('start');

let promise = new MyPromise((resolve, reject) => {
  console.log('step-');
  setTimeout(() => {
    resolve(123);
  }, 1000);
});

promise.then((value) => {
  console.log('step--');
  console.log('value', value);
});

console.log('end');

打印結果:

start
step-
end
step--
value1 123

通過以上步驟,一個最基本的Promise就已經實現完了,下面咱們會實現一些不在PromiseA+規範的擴展方法。

7. 實現catch()方法

then()方法的onFulfilledonRejected回調函數都不是必傳項,若是不傳,那麼咱們就沒法接收reject(reason)中的錯誤,這時咱們能夠經過鏈式調用catch()方法用來接收錯誤。舉例:

let promise = new Promise((resolve, reject) => {
  reject('has error');
});

promise.then((value) => {
  console.log('value', value);
}).catch((reason) => {
  console.log('reason', reason);
});

打印結果:

reason has error

不只如此,catch()能夠做爲Promise鏈式調用的最後一步,前面Promise發生的錯誤會冒泡到最後一個catch()中,從而捕獲異常。舉例:

let promise = new Promise((resolve, reject) => {
  resolve(123);
});

promise.then((value) => {
  console.log('value', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}).catch((reason) => {
  console.log('reason', reason);
});

打印結果:

value 123
reason has error1

那麼catch()方法究竟是如何實現的呢?

答案就是在Promise的實現中,onFulfilledonRejected函數是有默認值的:

MyPromise.js

MyPromise.prototype.then = function(onFuifilled, onRejected) {
  onFuifilled = typeof onFuifilled === 'function' ? onFuifilled : value => {return value;};
  onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
};

MyPromise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

能夠看到,onRejected的默認值是把錯誤reason經過throw拋出去。因爲咱們對於同步代碼的執行都是在try...catch中的,因此若是Promise發生了錯誤,若是沒傳onRejected,默認的函數會把錯誤reason拋出,而後會被promise2捕捉到,做爲reject(reason)決議。

catch()實現就是調用this.then(null, onRejected),因爲promise2reject,因此會執行onRejected回調,因而就捕捉到了第一個promise的錯誤。

總結來講,then()方法中不傳onRejected回調,Promise內部會默認幫你寫一個函數做爲回調,做用就是throw拋出reject或者try...catch到的錯誤,而後錯誤reason會被promise2做爲reject(reason)進行決議,因而會被下一個then()方法的onRejected回調函數調用,而catch只是寫了一個特殊的then(null, onRejected)而已。

因此,咱們在寫Promise的鏈式調用的時候,在then()中能夠不傳onRejected回調,只須要在鏈式調用的最末尾加一個catch()就能夠了,這樣在該鏈條中的Promise發生的錯誤都會被最後的catch捕獲到。

舉例1:

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  // 注意,不會走這裏,由於第一個promise是被reject的
  console.log('value1', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value2', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}, (reason) => {
  // 注意,這個then有onRejected回調
  console.log('reason2', reason);
}).catch((reason) => {
  // 錯誤在上一個then就被捕獲了,因此不會走到這裏
  console.log('reason3', reason);
});

打印結果:

reason2 123

舉例2:

let promise = new Promise((resolve, reject) => {
  reject(123);
});

promise.then((value) => {
  console.log('value1', value);
  return new Promise((resolve, reject) => {
    reject('has error1');
  });
}).then((value) => {
  console.log('value2', value);
  return new Promise((resolve, reject) => {
    reject('has error2');
  });
}).catch((reason) => {
  // 因爲鏈條中的then都沒有onRejected回調,因此會一直被冒泡到最後的catch這裏
  console.log('reason3', reason);
});

catchthen同樣都是返回一個新的Promise。有的同窗可能會有疑問,若是catch中的回調執行也發生錯誤該怎麼辦呢,這個咱們後續在Promise異常處理中再作討論。

打印結果:

reason3 123

8. 實現finally方法

finally是某些庫對Promise實現的一個擴展方法,不管是resolve仍是reject,都會走finally方法。

MyPromise.js

MyPromise.prototype.finally = function(fn) {
    return this.then(value => {
       fn();
       return value;
    }, reason => {
        fn();
        throw reason;
    });
};

9. 實現done方法

done方法做爲Promise鏈式調用的最後一步,用來向全局拋出沒有被Promise內部捕獲的錯誤,而且再也不返回一個Promise。通常用來結束一個Promise鏈。

MyPromise.js

MyPromise.prototype.done = function() {
    this.catch(reason => {
        console.log('done', reason);
        throw reason;
    });
};

10. 實現Promise.all方法

Promise.all()接收一個包含多個Promise的數組,當全部Promise均爲fulfilled狀態時,返回一個結果數組,數組中結果的順序和傳入的Promise順序一一對應。若是有一個Promiserejected狀態,則整個Promise.allrejected

MyPromise.js

MyPromise.all = function(promiseArr) {
  return new MyPromise((resolve, reject) => {
    let result = [];

    promiseArr.forEach((promise, index) => {
      promise.then((value) => {
        result[index] = value;

        if (result.length === promiseArr.length) {
          resolve(result);
        }
      }, reject);
    });
  });
};

test.js

let MyPromise = require('./MyPromise.js');

let promise1 = new MyPromise((resolve, reject) => {
  console.log('aaaa');
  setTimeout(() => {
    resolve(1111);
    console.log(1111);
  }, 1000);
});

let promise2 = new MyPromise((resolve, reject) => {
  console.log('bbbb');
  setTimeout(() => {
    reject(2222);
    console.log(2222);
  }, 2000);
});

let promise3 = new MyPromise((resolve, reject) => {
  console.log('cccc');
  setTimeout(() => {
    resolve(3333);
    console.log(3333);
  }, 3000);
});

Promise.all([promise1, promise2, promise3]).then((value) => {
  console.log('all value', value);
}, (reason) => {
  console.log('all reason', reason);
})

打印結果:

aaaa
bbbb
cccc
1111
2222
all reason 2222
3333

11. 實現Promise.race方法

Promise.race()接收一個包含多個Promise的數組,當有一個Promisefulfilled狀態時,整個大的Promiseonfulfilled,並執行onFulfilled回調函數。若是有一個Promiserejected狀態,則整個Promise.racerejected

MyPromise.js

MyPromise.race = function(promiseArr) {
  return new MyPromise((resolve, reject) => {
    promiseArr.forEach(promise => {
      promise.then((value) => {
        resolve(value);   
      }, reject);
    });
  });
};

test.js

let MyPromise = require('./MyPromise.js');

let promise1 = new MyPromise((resolve, reject) => {
  console.log('aaaa');
  setTimeout(() => {
    resolve(1111);
    console.log(1111);
  }, 1000);
});

let promise2 = new MyPromise((resolve, reject) => {
  console.log('bbbb');
  setTimeout(() => {
    reject(2222);
    console.log(2222);
  }, 2000);
});

let promise3 = new MyPromise((resolve, reject) => {
  console.log('cccc');
  setTimeout(() => {
    resolve(3333);
    console.log(3333);
  }, 3000);
});

Promise.race([promise1, promise2, promise3]).then((value) => {
  console.log('all value', value);
}, (reason) => {
  console.log('all reason', reason);
})

打印結果:

aaaa
bbbb
cccc
1111
all reason 1111
2222
3333

12. 實現Promise.resolve方法

Promise.resolve用來生成一個fulfilled完成態的Promise,通常放在整個Promise鏈的開頭,用來開始一個Promise鏈。

MyPromise.js

MyPromise.resolve = function(value) {
  let promise;

  promise = new MyPromise((resolve, reject) => {
    this.prototype.resolvePromise(promise, value, resolve, reject);
  });

  return promise;
};

test.js

let MyPromise = require('./MyPromise.js');

MyPromise.resolve(1111).then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(2222);
  })
}).then((value) => {
  console.log('value2', value);
})

打印結果:

value1 1111
value2 2222

因爲傳入的value有多是普通值,有多是thenable,也有多是另外一個Promise,因此調用resolvePromise進行解析。

12. 實現Promise.reject方法

Promise.reject用來生成一個rejected失敗態的Promise

MyPromise.js

MyPromise.reject = function(reason) {
  return new MyPromise((resolve, reject) => {
    reject(reason);
  });
};

test.js

let MyPromise = require('./MyPromise.js');

MyPromise.reject(1111).then((value) => {
  console.log('value1', value);
  return new MyPromise((resolve, reject) => {
    resolve(2222);
  })
}).then((value) => {
  console.log('value2', value);
}).catch(reason => {
  console.log('reason', reason);
});

打印結果:

reason 1111

13. 實現Promise.deferred方法

Promise.deferred能夠用來延遲執行resolvereject

MyPromise.js

MyPromise.deferred = function() {
    let dfd = {};
    dfd.promies = new MyPromise((resolve, reject) => {
      dfd.resolve = resolve;
      dfd.rfeject = reject;
    });
    return dfd;
};

這樣,你就能夠在外部經過調用dfd.resolve()dfd.reject()來決議該Promise

13. 如何中止一個Promise

假設這樣一個場景,咱們有一個很長的Promise鏈式調用,這些Promise是依次依賴的關係,若是鏈條中的某個Promise出錯了,就不須要再向下執行了,默認狀況下,咱們是沒法實現這個需求的,由於Promise不管是then仍是catch都會返回一個Promise,都會繼續向下執行thencatch。舉例:

new Promise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
}).catch()
  .then()
  .then()
  .catch()
  .then()

有沒有辦法讓這個鏈式調用在ERROR!!!的後面就停掉,徹底不去執行鏈式調用後面全部回調函數呢?

咱們本身封裝一個Promise.stop方法。

MyPromise.js

MyPromise.stop = function() {
  return new Promise(function() {});
};

stop中返回一個永遠不執行resolve或者rejectPromise,那麼這個Promise永遠處於pending狀態,因此永遠也不會向下執行thencatch了。這樣咱們就中止了一個Promise鏈。

new MyPromise(function(resolve, reject) {
  resolve(1111)
}).then(function(value) {
  // "ERROR!!!"
  MyPromise.stop();
}).catch()
  .then()
  .then()
  .catch()
  .then()

可是這樣會有一個缺點,就是鏈式調用後面的全部回調函數都沒法被垃圾回收器回收。

14. 如何解決Promise鏈上返回的最後一個Promise出現錯誤

看以下例子:

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
});

這裏a不存在,因此給a.b賦值是一個語法錯誤,onFulfilled回調函數是包在try...catch中執行的,錯誤會被catch到,可是因爲後面沒有thencatch了,這個錯誤沒法被處理,就會被Promise吃掉,沒有任何異常,這就是常說的Promise有可能會吃掉錯誤

那麼咱們怎麼處理這種狀況呢?

方法一

就是咱們前面已經實現過的done()

new Promise(function(resolve) {
  resolve(42)
}).then(function(value) {
  a.b = 2;
}).done();

done()方法至關於一個catch,可是卻再也不返回Promise了,注意done()方法中不能出現語法錯誤,不然又沒法捕獲了。

方法二

普通錯誤監聽windowerror事件能夠實現捕獲

window.addEventListener('error', error => {
  console.log(error); // 不會觸發
});

Promise沒有被onRejected()處理的錯誤須要監聽unhandledrejection事件

window.addEventListener('unhandledrejection', error => {
  console.log('unhandledrejection', error); // 能夠觸發,並且還能夠直接拿到 promise 對象
});

14. 單元測試

結束

相關單元測試以及完整代碼能夠到個人github查看,若是對你有幫助的話,就來個star吧~

單元測試

github

歡迎關注個人公衆號

微信公衆號

參考文檔

PromiseA+規範

相關文章
相關標籤/搜索