對實現Promise A+規範的polyfill 的講解

Promise A+ 規範


本篇僅講述Promise聲明時的運行過程和.then鏈式回調


聲明時運行過程

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 的狀態 注4:

如果 x 處於等待態, promise 需保持爲等待態直至 x 被執行或拒絕

如果 x 處於執行態,用相同的值執行 promise

如果 x 處於拒絕態,用相同的據因拒絕 promise

x 爲對象或函數

如果 x 爲對象或者函數:

把 x.then 賦值給 then 注5

如果取 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 注6。

大白話解釋:

let p = new Promise((res,rej)=>{

res(1);

});

//這部分就是用戶自定義的執行方法

(res,rej)=>{

res(1); //這就是解決過程

}

1即爲x;

這個時候,x==1;

執行後面的方法

[[Resolve]](p, 1) // 注:[[Resolve]]這個寫法類似obj-c

如果 x 有 then 方法且看上去像一個 Promise ,解決程序即嘗試使 promise 接受 x 的狀態;否則其用 x 的值來執行
promise 。

很明顯,這時候x沒有then方法,所以用 x 的值來執行p;

p這時候的value就是x;

p的state就是resolve,因爲是執行了res的解決過程

——————————————–歡樂模式—————————————-

簡單的解決過程

let p = new Promise2((res,rej)=>{
        res(1);
})
p.then(console.log);

Promise2源碼

const PENDING=Symbol();
const FULFILLED=Symbol();
const REJECTED=Symbol();
var Promise2 = function(func){
    this.value=null;
  this.state=PENDING;
  this.res_then=null;
  this.rej_then=null;

  goSafeResolveThen(this,func);
};
Promise2.prototype.then = function(res,rej){
    if(this.state===PENDING){
    this.res_then=res;
    this.rej_then=rej;
  }else{
    let resolver=this.state===FULFILLED? res : rej;
        resolver(this.value);
  }
}
function goSafeResolveThen(self,func){
    func(
      function(value){
        doResolve(self,value);
      },
      null  //不考慮失敗
    )
}
function doResolve(self,value){
    self.value=value;
    self.state=FULFILLED;
    if(self.res_then){
      self.res_then(value);
    }
}

JSBIN執行過程

————————————-難度飆升———————————————-

複雜的解決過程

let p = new Promise2((res,rej)=>{
    let temp = new Promise2((res,rej)=>{
        res(1);
    })
        res(temp);
})

p.then(console.log);

Promise2源碼

const PENDING=Symbol();
const FULFILLED=Symbol();
const REJECTED=Symbol();
var Promise2 = function(func){
    this.value=null;
  this.state=PENDING;
  this.id='Promise_'+parseInt(Math.random()*10000)
  this.res_then=null;
  this.rej_then=null;

  goSafeResolveThen(this,func);
};
Promise2.prototype.then = function(res,rej){
    if(this.state===PENDING){
    this.res_then=res;
    this.rej_then=rej;
  }else{
    let resolver=this.state===FULFILLED? res : rej;
        resolver(this.value);
  }
}
function goSafeResolveThen(self,func){
  console.log("哪個Promise在執行",self.id)
    func(
      function(value){
        doResolve(self,value);
      },
      null  //不考慮失敗
    )
}
function doResolve(self,value){
  let then = getThen(value);
  if(then){
    goSafeResolveThen(self,then);
  }else{
    self.value=value;
    self.state=FULFILLED;
    if(self.res_then){
        self.res_then(value);
    }
  }
}
function getThen(promise) {
  const then = promise && promise.then
  if(then){
    return function applyThen() {
      then.apply(promise, arguments)
    }
  }else{
    return null;
  }
}

JSBIN執行過程

————————————-地獄模式———————————————-

then鏈式回調

Then 方法

一個 promise 必須提供一個 then 方法以訪問其當前值、終值和據因。

調用時機

onFulfilled 和 onRejected 只有在執行環境堆棧僅包含平臺代碼時纔可被調用 注1

注1 這裏的平臺代碼指的是引擎、環境以及 promise 的實施代碼。實踐中要確保 onFulfilled 和 onRejected 方法異步執行,且應該在 then 方法被調用的那一輪事件循環之後的新執行棧中執行。這個事件隊列可以採用“宏任務(macro-task)”機制或者“微任務(micro-task)”機制來實現。由於 promise 的實施代碼本身就是平臺代碼(譯者注:即都是 JavaScript),故代碼自身在處理在處理程序時可能已經包含一個任務調度隊列。

返回

then 方法必須返回一個 promise 對象 注3

promise2 = promise1.then(onFulfilled, onRejected);

如果 onFulfilled 或者 onRejected 返回一個值 x ,則運行下面的 Promise 解決過程:[[Resolve]](promise2, x)

如果 onFulfilled 或者 onRejected 拋出一個異常 e ,則 promise2 必須拒絕執行,並返回拒因 e

如果 onFulfilled 不是函數且 promise1 成功執行, promise2 必須成功執行並返回相同的值

如果 onRejected 不是函數且 promise1 拒絕執行, promise2 必須拒絕執行並返回相同的據因

譯者注:理解上面的“返回”部分非常重要,即:不論 promise1 被 reject 還是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected。

大白話解釋:

執行then方法時,返回一個pending狀態的Promise注:命名爲 _tempPromise

_tempPromise這時候的value是void 0,狀態是pending,

當同步任務都執行完成後,

then方法裏面的宏任務或者微任務開啓執行,執行onFulfilled或者onRejected

這個時候_tempPromise的value被改變,狀態是resolve或者rejected;

//執行
let p = new Promise2((res,rej)=>{
    res(1);
})
let p2 = p.then((value)=>{
    console.log(value);
})
console.log("p2.state:",p2.state);
let p3 =p2.then((value)=>{
    console.log('到這了:',value);
})
console.log("p3.state:",p3.state);

Promise2源碼

const PENDING=Symbol('PENDING');
const FULFILLED=Symbol('FULFILLED');
const REJECTED=Symbol('REJECTED');
function INTERNAL() {}
var Promise2 = function(func){
  this.value=null;
  this.state=PENDING;
  this.id='Promise_'+parseInt(Math.random()*10000)
  this._thenObj=null;
  //這個判斷的深意
  if(func!==INTERNAL){
    goSafeResolveThen(this,func);
  }
};
Promise2.prototype.then = function(res,rej){
    let promise = new this.constructor(INTERNAL);
    console.log("new id:",promise.id)
    if(this.state===PENDING){
        this._thenObj=new newThenObj(promise,res,rej);
    }else{
        let resolver=this.state===FULFILLED? res : rej;
        unwrap(promise,resolver,this.value);
    }
    return promise;
}
/**
 * .then時候聲明一個新解決對象
 * @param {*} promise 
 * @param {*} res 
 * @param {*} rej 
 */
function newThenObj(promise,res,rej){
    this.promise = promise
    this.callFulfilled = function (value) {
        unwrap(this.promise, res, value)
    }
}
/**
 * 讓then執行進入宏(微)任務
 * @param {*} promise 
 * @param {*} fulfilled 
 * @param {*} value 
 */
function unwrap(promise,fulfilled,value){
    //setTimeout(()=>{
    //    let returnValue=fulfilled(value);
    //    doResolve(promise,returnValue);
    //})
    process.nextTick(()=>{
         let returnValue=fulfilled(value);
         doResolve(promise,returnValue);
    })
}
function goSafeResolveThen(self,func){
  console.log("哪個Promise在執行",self.id)
    func(
      function(value){
        doResolve(self,value);
      },
      null  //不考慮失敗
    )
}
function doResolve(self,value){
  let then = getThen(value);
  if(then){
    goSafeResolveThen(self,then);
  }else{
    self.value=value;
    self.state=FULFILLED;
    if(self._thenObj){
        self._thenObj.callFulfilled(value);
    }
  }
}
function getThen(promise) {
  const then = promise && promise.then
  if(then){
    return function applyThen() {
      then.apply(promise, arguments)
    }
  }else{
    return null;
  }
}

地獄模式JSBIN

————————————-噩夢模式—————————

//執行
let p = new Promise2((res,rej)=>{
    res(1);
})
let p2 = p.then((value)=>{
    console.log(value);
    return new Promise2((res,rej)=>{
        res(2);
    })
})
console.log("p2.state:",p2.state);
let p3 =p2.then((value)=>{
    console.log(value);
})
console.log("p3.state:",p3.state);
let p4 = p3.then(value=>{
    console.log('到這了',value);
})
console.log("p4.state:",p4.state);

噩夢模式的JSBIN

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章