欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳細解讀JavaScript編程中的Promise使用

 更新時間:2015年07月27日 18:17:57   作者:梁砫  
這篇文章主要介紹了詳細解讀JavaScript編程中的Promise使用,是JS入門學習中的基礎知識,需要的朋友可以參考下

Promise核心說明

盡管Promise已經(jīng)有自己的規(guī)范,但目前的各類Promise庫,在Promise的實現(xiàn)細節(jié)上是有差異的,部分API甚至在意義上完全不同。但Promise的核心內(nèi)容,是相通的,它就是then方法。在相關術語中,promise指的就是一個有then方法,且該方法能觸發(fā)特定行為的對象或函數(shù)。

Promise可以有不同的實現(xiàn)方式,因此Promise核心說明并不會討論任何具體的實現(xiàn)代碼。

先閱讀Promise核心說明的意思是:看,這就是需要寫出來的結果,請參照這個結果想一想怎么用代碼寫出來吧。
起步:用這一種方式理解Promise

回想一下Promise解決的是什么問題?回調(diào)。例如,函數(shù)doMission1()代表第一件事情,現(xiàn)在,我們想要在這件事情完成后,再做下一件事情doMission2(),應該怎么做呢?

先看看我們常見的回調(diào)模式。doMission1()說:“你要這么做的話,就把doMission2()交給我,我在結束后幫你調(diào)用。”所以會是:

doMission1(doMission2);

Promise模式又是如何呢?你對doMission1()說:“不行,控制權要在我這里。你應該改變一下,你先返回一個特別的東西給我,然后我來用這個東西安排下一件事?!边@個特別的東西就是Promise,這會變成這樣:

doMission1().then(doMission2);

可以看出,Promise將回調(diào)模式的主從關系調(diào)換了一個位置(翻身做主人!),多個事件的流程關系,就可以這樣集中到主干道上(而不是分散在各個事件函數(shù)之內(nèi))。

好了,如何做這樣一個轉換呢?從最簡單的情況來吧,假定doMission1()的代碼是:

function doMission1(callback){
  var value = 1;
  callback(value);
}

那么,它可以改變一下,變成這樣:

function doMission1(){
  return {
    then: function(callback){
      var value = 1;
      callback(value);
    }
  };
}

這就完成了轉換。雖然并不是實際有用的轉換,但到這里,其實已經(jīng)觸及了Promise最為重要的實現(xiàn)要點,即Promise將返回值轉換為帶then方法的對象。
進階:Q的設計路程
從defer開始

design/q0.js是Q初步成型的第一步。它創(chuàng)建了一個名為defer的工具函數(shù),用于創(chuàng)建Promise:

var defer = function () {
  var pending = [], value;
  return {
    resolve: function (_value) {
      value = _value;
      for (var i = 0, ii = pending.length; i < ii; i++) {
        var callback = pending[i];
        callback(value);
      }
      pending = undefined;
    },
    then: function (callback) {
      if (pending) {
        pending.push(callback);
      } else {
        callback(value);
      }
    }
  }
};

這段源碼可以看出,運行defer()將得到一個對象,該對象包含resolve和then兩個方法。請回想一下jQuery的Deferred(同樣有resolve和then),這兩個方法將會是近似的效果。then會參考pending的狀態(tài),如果是等待狀態(tài)則將回調(diào)保存(push),否則立即調(diào)用回調(diào)。resolve則將肯定這個Promise,更新值的同時運行完所有保存的回調(diào)。defer的使用示例如下:

var oneOneSecondLater = function () {
  var result = defer();
  setTimeout(function () {
    result.resolve(1);
  }, 1000);
  return result;
};

oneOneSecondLater().then(callback);

這里oneOneSecondLater()包含異步內(nèi)容(setTimeout),但這里讓它立即返回了一個defer()生成的對象,然后將對象的resolve方法放在異步結束的位置調(diào)用(并附帶上值,或者說結果)。

到此,以上代碼存在一個問題:resolve可以被執(zhí)行多次。因此,resolve中應該加入對狀態(tài)的判斷,保證resolve只有一次有效。這就是Q下一步的design/q1.js(僅差異部分):

resolve: function (_value) {
  if (pending) {
    value = _value;
    for (var i = 0, ii = pending.length; i < ii; i++) {
      var callback = pending[i];
      callback(value);
    }
    pending = undefined;
  } else {
    throw new Error("A promise can only be resolved once.");
  }
}

對第二次及更多的調(diào)用,可以這樣拋出一個錯誤,也可以直接忽略掉。
分離defer和promise

在前面的實現(xiàn)中,defer生成的對象同時擁有then方法和resolve方法。按照定義,promise關心的是then方法,至于觸發(fā)promise改變狀態(tài)的resolve,是另一回事。所以,Q接下來將擁有then方法的promise,和擁有resolve的defer分離開來,各自獨立使用。這樣就好像劃清了各自的職責,各自只留一定的權限,這會使代碼邏輯更明晰,易于調(diào)整。請看design/q3.js:(q2在此跳過)

var isPromise = function (value) {
  return value && typeof value.then === "function";
};

var defer = function () {
  var pending = [], value;
  return {
    resolve: function (_value) {
      if (pending) {
        value = _value;
        for (var i = 0, ii = pending.length; i < ii; i++) {
          var callback = pending[i];
          callback(value);
        }
        pending = undefined;
      }
    },
    promise: {
      then: function (callback) {
        if (pending) {
          pending.push(callback);
        } else {
          callback(value);
        }
      }
    }
  };
};

如果你仔細對比一下q1,你會發(fā)現(xiàn)區(qū)別很小。一方面,不再拋出錯誤(改為直接忽略第二次及更多的resolve),另一方面,將then方法移動到一個名為promise的對象內(nèi)。到這里,運行defer()得到的對象(就稱為defer吧),將擁有resolve方法,和一個promise屬性指向另一個對象。這另一個對象就是僅有then方法的promise。這就完成了分離。

前面還有一個isPromise()函數(shù),它通過是否有then方法來判斷對象是否是promise(duck-typing的判斷方法)。為了正確使用和處理分離開的promise,會像這樣需要將promise和其他值區(qū)分開來。
實現(xiàn)promise的級聯(lián)

接下來會是相當重要的一步。到前面到q3為止,所實現(xiàn)的promise都是不能級聯(lián)的。但你所熟知的promise應該支持這樣的語法:

promise.then(step1).then(step2);

以上過程可以理解為,promise將可以創(chuàng)造新的promise,且取自舊的promise的值(前面代碼中的value)。要實現(xiàn)then的級聯(lián),需要做到一些事情:

  •     then方法必須返回promise。
  •     這個返回的promise必須用傳遞給then方法的回調(diào)運行后的返回結果,來設置自己的值。
  •     傳遞給then方法的回調(diào),必須返回一個promise或值。

design/q4.js中,為了實現(xiàn)這一點,新增了一個工具函數(shù)ref:

var ref = function (value) {
  if (value && typeof value.then === "function")
    return value;
  return {
    then: function (callback) {
      return ref(callback(value));
    }
  };
};

這是在著手處理與promise關聯(lián)的value。這個工具函數(shù)將對任一個value值做一次包裝,如果是一個promise,則什么也不做,如果不是promise,則將它包裝成一個promise。注意這里有一個遞歸,它確保包裝成的promise可以使用then方法級聯(lián)。為了幫助理解它,下面是一個使用的例子:

ref("step1").then(function(value){
  console.log(value); // "step1"
  return 15;
}).then(function(value){
  console.log(value); // 15
});

你可以看到value是怎樣傳遞的,promise級聯(lián)需要做到的也是如此。

design/q4.js通過結合使用這個ref函數(shù),將原來的defer轉變?yōu)榭杉壜?lián)的形式:

var defer = function () {
  var pending = [], value;
  return {
    resolve: function (_value) {
      if (pending) {
        value = ref(_value); // values wrapped in a promise
        for (var i = 0, ii = pending.length; i < ii; i++) {
          var callback = pending[i];
          value.then(callback); // then called instead
        }
        pending = undefined;
      }
    },
    promise: {
      then: function (_callback) {
        var result = defer();
        // callback is wrapped so that its return
        // value is captured and used to resolve the promise
        // that "then" returns
        var callback = function (value) {
          result.resolve(_callback(value));
        };
        if (pending) {
          pending.push(callback);
        } else {
          value.then(callback);
        }
        return result.promise;
      }
    }
  };
};

原來callback(value)的形式,都修改為value.then(callback)。這個修改后效果其實和原來相同,只是因為value變成了promise包裝的類型,會需要這樣調(diào)用。

then方法有了較多變動,會先新生成一個defer,并在結尾處返回這個defer的promise。請注意,callback不再是直接取用傳遞給then的那個,而是在此基礎之上增加一層,并把新生成的defer的resolve方法放置在此。此處可以理解為,then方法將返回一個新生成的promise,因此需要把promise的resolve也預留好,在舊的promise的resolve運行后,新的promise的resolve也會隨之運行。這樣才能像管道一樣,讓事件按照then連接的內(nèi)容,一層一層傳遞下去。
加入錯誤處理

promise的then方法應該可以包含兩個參數(shù),分別是肯定和否定狀態(tài)的處理函數(shù)(onFulfilled與onRejected)。前面我們實現(xiàn)的promise還只能轉變?yōu)榭隙顟B(tài),所以,接下來應該加入否定狀態(tài)部分。

請注意,promise的then方法的兩個參數(shù),都是可選參數(shù)。design/q6.js(q5也跳過)加入了工具函數(shù)reject來幫助實現(xiàn)promise的否定狀態(tài):

var reject = function (reason) {
  return {
    then: function (callback, errback) {
      return ref(errback(reason));
    }
  };
};

它和ref的主要區(qū)別是,它返回的對象的then方法,只會取第二個參數(shù)的errback來運行。design/q6.js的其余部分是:

var defer = function () {
  var pending = [], value;
  return {
    resolve: function (_value) {
      if (pending) {
        value = ref(_value);
        for (var i = 0, ii = pending.length; i < ii; i++) {
          value.then.apply(value, pending[i]);
        }
        pending = undefined;
      }
    },
    promise: {
      then: function (_callback, _errback) {
        var result = defer();
        // provide default callbacks and errbacks
        _callback = _callback || function (value) {
          // by default, forward fulfillment
          return value;
        };
        _errback = _errback || function (reason) {
          // by default, forward rejection
          return reject(reason);
        };
        var callback = function (value) {
          result.resolve(_callback(value));
        };
        var errback = function (reason) {
          result.resolve(_errback(reason));
        };
        if (pending) {
          pending.push([callback, errback]);
        } else {
          value.then(callback, errback);
        }
        return result.promise;
      }
    }
  };
};

這里的主要改動是,將數(shù)組pending只保存單個回調(diào)的形式,改為同時保存肯定和否定的兩種回調(diào)的形式。而且,在then中定義了默認的肯定和否定回調(diào),使得then方法滿足了promise的2個可選參數(shù)的要求。

你也許注意到defer中還是只有一個resolve方法,而沒有類似jQuery的reject。那么,錯誤處理要如何觸發(fā)呢?請看這個例子:

var defer1 = defer(),
promise1 = defer1.promise;
promise1.then(function(value){
  console.log("1: value = ", value);
  return reject("error happens"); 
}).then(function(value){
  console.log("2: value = ", value);
}).then(null, function(reason){
  console.log("3: reason = ", reason);
});
defer1.resolve(10);

// Result:
// 1: value = 10
// 3: reason = error happens

可以看出,每一個傳遞給then方法的返回值是很重要的,它將決定下一個then方法的調(diào)用結果。而如果像上面這樣返回工具函數(shù)reject生成的對象,就會觸發(fā)錯誤處理。
融入異步

終于到了最后的design/q7.js。直到前面的q6,還存在一個問題,就是then方法運行的時候,可能是同步的,也可能是異步的,這取決于傳遞給then的函數(shù)(例如直接返回一個值,就是同步,返回一個其他的promise,就可以是異步)。這種不確定性可能帶來潛在的問題。因此,Q的后面這一步,是確保將所有then轉變?yōu)楫惒健?/p>

design/q7.js定義了另一個工具函數(shù)enqueue:

var enqueue = function (callback) {
  //process.nextTick(callback); // NodeJS
  setTimeout(callback, 1); // Na?ve browser solution
};

顯然,這個工具函數(shù)會將任意函數(shù)推遲到下一個事件隊列運行。

design/q7.js其他的修改點是(只顯示修改部分):

var ref = function (value) {
  // ...
  return {
    then: function (callback) {
      var result = defer();
      // XXX
      enqueue(function () {
        result.resolve(callback(value));
      });
      return result.promise;
    }
  };
};

var reject = function (reason) {
  return {
    then: function (callback, errback) {
      var result = defer();
      // XXX
      enqueue(function () {
        result.resolve(errback(reason));
      });
      return result.promise;
    }
  };
};

var defer = function () {
  var pending = [], value;
  return {
    resolve: function (_value) {
      // ...
          enqueue(function () {
            value.then.apply(value, pending[i]);
          });
      // ...
    },
    promise: {
      then: function (_callback, _errback) {
          // ...
          enqueue(function () {
            value.then(callback, errback);
          });
          // ...
      }
    }
  };
};

即把原來的value.then的部分,都轉變?yōu)楫惒健?/p>

到此,Q提供的Promise設計原理q0~q7,全部結束。
結語

即便本文已經(jīng)是這么長的篇幅,但所講述的也只到基礎的Promise。大部分Promise庫會有更多的API來應對更多和Promise有關的需求,例如all()、spread(),不過,讀到這里,你已經(jīng)了解了實現(xiàn)Promise的核心理念,這一定對你今后應用Promise有所幫助。

在我看來,Promise是精巧的設計,我花了相當一些時間才差不多理解它。Q作為一個典型Promise庫,在思路上走得很明確。可以感受到,再復雜的庫也是先從基本的要點開始的,如果我們自己要做類似的事,也應該保持這樣的心態(tài)一點一點進步。

相關文章

  • PHP守護進程實例

    PHP守護進程實例

    這篇文章主要介紹了PHP守護進程實例,php也是可以直接進行守護進程的啟動與終止的,相對于shell來說會簡單很多,需要的朋友可以參考下
    2015-03-03
  • JavaScript進階教程(第二課)

    JavaScript進階教程(第二課)

    JavaScript進階教程(第二課)...
    2007-04-04
  • js parsefloat parseint 轉換函數(shù)

    js parsefloat parseint 轉換函數(shù)

    JavaScript提供了3個顯式的類型轉換函數(shù),分別是eval()、parseInt()和parseFloat()。
    2010-01-01
  • js數(shù)組操作常用方法

    js數(shù)組操作常用方法

    這篇文章主要介紹了js數(shù)組操作常用方法,包括數(shù)組的創(chuàng)建、數(shù)組的元素的訪問、數(shù)組元素的刪除、數(shù)組的拷貝,需要的朋友可以參考下
    2014-05-05
  • JavaScript DOM進階方法

    JavaScript DOM進階方法

    這篇文章主要介紹了JavaScript DOM進階,需要的朋友可以參考下
    2015-04-04
  • 詳解參數(shù)傳遞四種形式

    詳解參數(shù)傳遞四種形式

    這篇文章主要介紹了參數(shù)傳遞的四種形式(URL,超鏈接,js,form表單)有需要的朋友可以參考下
    2015-07-07
  • JavaScript中reduce()方法的使用詳解

    JavaScript中reduce()方法的使用詳解

    這篇文章主要介紹了JavaScript中reduce()方法的使用詳解,是JS入門學習中的基礎知識,需要的朋友可以參考下
    2015-06-06
  • JavaScript 學習筆記(十四) 正則表達式

    JavaScript 學習筆記(十四) 正則表達式

    RegExp類 RegExp對象的構造函數(shù)可以帶一個或兩個參數(shù) 第一個參數(shù)是描述需要進行匹配的模式字符串,如果還有第二個參數(shù),這個參數(shù)則制定了額外的處理指令。
    2010-01-01
  • javascript列表框操作函數(shù)集合匯總

    javascript列表框操作函數(shù)集合匯總

    這篇文章主要是對javascript列表框操作函數(shù)進行了詳細的匯總介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2013-11-11
  • 如何學習Javascript入門指導

    如何學習Javascript入門指導

    首先要說明的是,咱現(xiàn)在不是高手,最多還是一個半桶水,算是入了JS的門
    2013-11-11

最新評論