1. 程式人生 > >異步解決方案promise及源碼實現

異步解決方案promise及源碼實現

bubuko 別人 else 簡單 rac 帶來 用法 res fill

js語言的特性,造就了特別的異步處理方式,我記得以前用的最多的就是回調函數,那個時候寫jquery的ajax時候,特別喜歡寫這種代碼:

$.ajax({

  method:‘get‘,

  url:"http://text/api",

  success:function(){

      $.ajax({

        method:‘get‘,

        url:"http://text/api",

        success:function(){

      }

    });

  }

});

後一個ajax的發送需要依賴前面的ajax的返回,也許有的朋友說還好啊,其實一兩個確實還好,但是多了就比較暈。不直觀。後面調試起來有點麻煩。後來很多瀏覽器就自己實現了一種promise原生對象,這個對象提供一系列方法,來解決這種回調帶來的不直觀,不易維護的問題。

  首先,這個promise很多瀏覽器已經實現了,開發者只需要調用就可以,就像window,document之類的一樣。首先介紹一下它的用法吧!

  

技術分享圖片


  我們需要通過new調用Promise對象,這個對象需要傳入一個函數,函數有兩個參數,分別是resolve,reject,分別代表成功和失敗兩種狀態,同時還有一個then的方法,它也有兩函數作為參數,第一個表示任務處理成功的後續操作,第二個表示任務處理失敗的後續操作,當我們調用實例上的then 的方法後,promise會根據我們在new Promise過程中調用的是resolve還是reject來判斷應該執行哪一個函數。

  這樣我們可以簡單粗暴的改寫一個上面那個ajax的代碼

  技術分享圖片

  這樣一來代碼變的更加明了,而且then方法可以一直鏈式調用下去,不管有多少異步任務都可以通過then方法寫下去。

  接下來,我們看看怎麽模擬這種操作,寫一個自己的promise。

  首先,我們需要設定三種狀態,成功,失敗 和 等待,也就是一個任務完成的情況,做完了就是成功,沒做好就是失敗,還沒開始做就是等待。

技術分享圖片

技術分享圖片

這就完成了一個promise最基本的功能,此時,如果在excutor函數中,出現一個異步任務,需要等待一秒才能執行resolve或者reject,此時狀態會處於pending,我們需要做一個數組,把這些等著狀態的任務裝起來,在reject或者resolve執行時候,遍歷數組,一個一個依次執行。

技術分享圖片

技術分享圖片

接下來增加鏈式調用功能

技術分享圖片

技術分享圖片

技術分享圖片

到此我們還要解決用戶在then方法中隨意返回數據的問題,用戶可能返回一個普通值,也可能返回一個新的promise,因此還需要作進一步處理

function Promise(executor) { // executor是一個執行函數
let self = this;
self.status = ‘pending‘;
self.value = undefined; // 默認成功的值
self.reason = undefined; // 默認失敗的原因
self.onResolvedCallbacks = []; // 存放then成功的回調
self.onRejectedCallbacks = []; // 存放then失敗的回調
function resolve(value) { // 成功狀態
if (self.status === ‘pending‘) {
self.status = ‘resolved‘;
self.value = value;
self.onResolvedCallbacks.forEach(function (fn) {
fn();
});
}
}
function reject(reason) { // 失敗狀態
if (self.status === ‘pending‘) {
self.status = ‘rejected‘;
self.reason = reason;
self.onRejectedCallbacks.forEach(function (fn) {
fn();
})
}
}
try {
executor(resolve, reject)
} catch (e) { // 捕獲的時候發生異常,就直接失敗了
reject(e);
}
}
function resolvePromise(p2,x,resolve,reject){
// 有可能這裏返回的x是別人的promise
// 盡可能允許其他亂寫
if(p2===x){ //這裏應該報一個類型錯誤,有問題
return reject(new TypeError(‘循環引用了‘))
}
// 看x是不是一個promise,promise應該是一個對象
if(x!==null||(typeof x === ‘object‘||typeof x === ‘function‘)){
// 可能是promise {},看這個對象中是否有then方法,如果有then我就認為他是promise了
try{ // {then:1}
let then = x.then;
if(typeof then === ‘function‘){
// 成功
then.call(x,function(y){
// y可能還是一個promise,在去解析直到返回的是一個普通值
resolvePromise(promise2,y,resolve,reject)
},function(err){ //失敗
reject(err);
})
}else{
resolve(x)
}
}catch(e){
reject(e);
}
}else{ // 說明是一個普通值1
resolve(x); // 表示成功了
}
}
Promise.prototype.then = function (onFulfilled, onRjected) {
let self = this;
let promise2; //返回的promise
if (self.status === ‘resolved‘) {
promise2 = new Promise(function (resolve, reject) {
// 當成功或者失敗執行時有異常那麽返回的promise應該處於失敗狀態
// x可能是一個promise 也有可能是一個普通的值
let x = onFulfilled(self.value);
// x可能是別人promise,寫一個方法統一處理
resolvePromise(promise2,x,resolve,reject);
})
}
if (self.status === ‘rejected‘) {
promise2 = new Promise(function (resolve, reject) {
let x = onRjected(self.reason);
resolvePromise(promise2,x,resolve,reject);
})
}
// 當調用then時可能沒成功 也沒失敗
if (self.status === ‘pending‘) {
promise2 = new Promise(function (resolve, reject) {
// 此時沒有resolve 也沒有reject
self.onResolvedCallbacks.push(function () {
let x = onFulfilled(self.value);
resolvePromise(promise2,x,resolve,reject);
});
self.onRejectedCallbacks.push(function () {
let x = onRjected(self.reason);
resolvePromise(promise2,x,resolve,reject);
});
})
}
return promise2;
}
// mjs
module.exports = Promise

到此基本完成promise的基本功能,但還有一些方法沒有實現,比如race ,all,也沒有實現同時調用reject和resolve的問題,後續有時間再處理。有問題請指正

  

異步解決方案promise及源碼實現