手寫一個PromiseA+的實現

Promise

手寫一個PromiseA+的實現。注意這裏只是模擬,實際上原生的promise在事件隊列中屬於microTask。這裏用setTimeout模擬不是特別恰當。由於setTimeout是一個macroTask。git

1. 最簡單的基本功能

/**
 * 定義Promise
 * 先實現一個最簡單的。用setTimeout模擬一個異步的請求。
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push(onFulfilled);
  }

  function resolve(value){
    callbacks.forEach(function(cb){
      cb(value);
    })
  }

  fn(resolve);
}


// 使用Promise
var p = new Promise(function(resolve){
  setTimeout(function(){
    resolve('這是響應的數據')
  },2000)
})

p.then(function(response){
  console.log(response);
})

2.鏈式調用

/**
 * 先看一下前一個例子存在的問題
 * 1.在前一個例子中不斷調用then須要支持鏈式調用,每次執行then都要返回調用對象自己。
 * 2.在前一個例子中,當鏈式調用的時候,每次then中的值都是同一個值,這是有問題的。其實第一次then中的返回值,應該是第二次調用then中的函數的參數,依次類推。
 * 因此,咱們進一步優化一下代碼。
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    callbacks.map(function(cb,index){
      if(index === 0){
        callbacks[index].value = value;
      }
      var rsp = cb.f(cb.value);
      if(typeof callbacks[index+1] !== 'undefined'){
        callbacks[index+1].value = rsp;
      }
    })
  }
  fn(resolve);
}


// 使用Promise
var p = new Promise(function(resolve){
  setTimeout(function(){
    resolve('這是響應的數據')
  },2000)
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

3. 異步

/**
 * 先看一下前一個例子存在的問題
 * 1. 若是在then方法註冊回調以前,resolve函數就執行了,怎麼辦?好比 new Promise的時候傳入的函數是同步函數的話,
 * then還沒被註冊,resolve就執行了。。這在PromiseA+規範中是不容許的,規範明確要求回調須要經過異步的方式執行。
 * 用來保證一致可靠的執行順序。
 * 
 * 所以咱們須要加入一些處理。把resolve裏的代碼放到異步隊列中去。這裏咱們利用setTimeout來實現。
 * 原理就是經過setTimeout機制,將resolve中執行回調的邏輯放置到JS任務隊列末尾,以保證在resolve執行時,
 * then方法的回調函數已經註冊完成
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = value;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 使用Promise,如今即便是同步的立馬resolve,也能正常運行了。
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

4. 狀態機制

/**
 * 先看一下前一個例子存在的問題
 * 1.前一個例子還存在一些問題,若是Promise異步操做已經成功,在這以前註冊的全部回調都會執行,
 * 可是在這以後再註冊的回調函數就不再執行了。具體的運行下面這段代碼,能夠看到「can i invoke」並無打印出來
 * 想要解決這個問題,咱們就須要加入狀態機制了。具體實現看本文件夾下的另外一個js文件裏的代碼。
 * 
 */
function Promise(fn){
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    callbacks.push({f:onFulfilled});
    return this;
  }

  function resolve(value){
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = value;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},0)
/**
 * 在promise01.js中,咱們已經分析了,咱們須要加入狀態機制
 * 在這裏實現一下PromiseA+中關於狀態的規範。
 * 
 * Promises/A+規範中的2.1Promise States中明確規定了,pending能夠轉化爲fulfilled或rejected而且只能轉化一次,
 * 也就是說若是pending轉化到fulfilled狀態,那麼就不能再轉化到rejected。
 * 而且fulfilled和rejected狀態只能由pending轉化而來,二者之間不能互相轉換
 * 
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {
    // 若是是pending狀態,則加入到註冊隊列中去。
    if(status === 'pending'){
      callbacks.push({f:onFulfilled});
      return this;
    }
    // 若是是fulfilled 狀態,此時直接執行傳入的註冊函數便可。
    onFulfilled(value);
    return this;
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = newValue;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)
/**
 * 剛纔的例子中,確實打印出了 can i invoke,可是以前then的註冊函數的返回值,並無打印出來。
 * 也就是說 1 和 2 並無被打印出來,看下面的註釋
 * 
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  this.then = function(onFulfilled) {

    if(status === 'pending'){
      callbacks.push({f:onFulfilled});
      return this;
    }

    onFulfilled(value);
    return this;
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    setTimeout(function(){
        callbacks.map(function(cb,index){
          if(index === 0){
            callbacks[index].value = newValue;
          }
          var rsp = cb.f(cb.value);
          if(typeof callbacks[index+1] !== 'undefined'){
            callbacks[index+1].value = rsp;
          }
        })
    },0)
  }
  fn(resolve);
}

var p = new Promise(function(resolve){
    resolve('aaaaaa')
})

p.then(function(response){
  console.log(response);    
  return 1;
}).then(function(response){
  console.log(response);  // 這裏應該打印的是45行返回的1,可是打印出來的確是aaaaaa
  return 2;  
}).then(function(response){
  console.log(response); // 這裏應該打印的是48行返回的2,可是打印出來的確是aaaaaa
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)


/**
 * 問題的根源在於什麼呢?
 * 問題的根源是每次的then的返回值都是p,當狀態是fulfilled,執行的是onFulfilled(value)
 * 此處的value是p的value,也就是fulfilled狀態的value。根據規範,promise應該是隻能發射單值。
 * 而咱們設計了一個callback堆棧中有一系列的值。生生的把promise變成了多值發射。
 * 
 * 因此,調整思路,每一個then都應該返回一個promise,這個promise應該是一個全新的promise。
 * 具體實現見下一個例子。
 */
/**
 * 根據剛纔的分析,咱們從新優化一下代碼
 * 1.去掉以前的多值設計
 * 2.每次的then 返回的都是一個全新的promise
 *
 */
function Promise(fn){
  var status = 'pending'
  var value= null;
  var callbacks = [];
  var self = this;

  this.then = function(onFulfilled) {
    return new Promise(function(resolve){
      function handle(value){
        var res = typeof onFulfilled === 'function' ? onFulfilled(value) : value;
        resolve(res);
      }
      // 若是是pending狀態,則加入到註冊隊列中去。
      if(status === 'pending'){
        callbacks.push(handle);
      // 若是是fulfilled 狀態。
      }else if(status === 'fulfilled'){
          handle(value);
      }
    })
  }

  function resolve(newValue){
    value = newValue;
    status = 'fulfilled';
    
    setTimeout(function(){
        callbacks.map(function(cb){
          cb(value);
        })
    },0)
  };

  fn(resolve);
}


// 
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)



/**
 * 運行一下,完美輸出
 * 先是輸出「這是響應的數據」,而後是「1」,而後是「2」, 而後是「can i invoke?」
 * 
 * 接下來咱們要好好整理一下代碼了。把一些公用的方法放到構造函數的原型上去。改造以後的例子見下一個例子
 */
/**
 * 根據剛纔的分析,咱們從新優化一下代碼
 * 1.把私有屬性掛到實例上去
 * 2.把公共方法掛到構造函數的原型上去
 *
 */
function Promise(fn){
  this.status = 'pending';
  this.value= null;
  this.callbacks = [];
  var self = this;
  function resolve(newValue){
    self.value = newValue;
    self.status = 'fulfilled';
    setTimeout(function(){
      self.callbacks.map(function(cb){
          cb(value);
        })
    },0)
  }
  fn(resolve);
}


Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;


Promise.prototype.then = function(onFulfilled){
  var self = this;
  return new Promise(function(resolve){
    function handle(value){
      var res = typeof onFulfilled === 'function'?  onFulfilled(value) : value;
      resolve(res);
    }
    if(self.status==='pending'){
      self.callbacks.push(handle);
    }else if(self.status ==='fulfilled'){
      handle(self.value);
    }
  })
}

// 使用
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return 1;
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
   })
},1000)

5.處理註冊的函數返回值是promise的狀況

/**
 * 不出意料,又要拋出問題了。當then註冊的回調函數返回的是promise的時候,從這個then以後的全部then的註冊函數
 * 都應該註冊在新返回的promise上。直到遇到下一個回調函數的返回值也是promise。
 * 
 * 實現思路:
 * 在handle中判斷註冊函數返回的是不是promise。若是是的話,則resolve這個返回的promise的值,具體代碼看一下36到38行
 * 
 */
function Promise(fn){
  this.status = 'pending';
  this.value= null;
  this.callbacks = [];
  var self = this;
  function resolve(newValue){
    self.value = newValue;
    self.status = 'fulfilled';
    setTimeout(function(){
      self.callbacks.map(function(cb){
          cb(value);
        })
    },0)
  }
  fn(resolve);
}


Promise.prototype = Object.create(null);
Promise.prototype.constructor = Promise;


Promise.prototype.then = function(onFulfilled){
  var self = this;
  var promise = new Promise(function(resolve){
    function handle(value){
      var res = typeof onFulfilled === 'function'?  onFulfilled(value) : value;
      if(res instanceof Promise){
        promise = res;
        resolve(res.value);
      }else {
        resolve(res);
      }
    }
    if(self.status==='pending'){
      self.callbacks.push(handle);
    }else if(self.status ==='fulfilled'){
      handle(self.value);
    }
  })
  return promise;
}

// 使用
var p = new Promise(function(resolve){
    resolve('這是響應的數據')
})

p.then(function(response){
  console.log(response);
  return new Promise(function(resolve){
    resolve('testtest')
  })
}).then(function(response){
  console.log(response);
  return 2;  
}).then(function(response){
  console.log(response);
})

setTimeout(function(){
   p.then(function(response){
     console.log('can i invoke?');
     return new Promise(function(resolve){
        resolve('hhhhhh')
      })
   }).then(function(response){
     console.log(response);
   })
},1000)

源碼所有在github上:https://github.com/JesseZhao1...github

相關文章
相關標籤/搜索