亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript編程中的Promise使用大全

2019-11-20 11:58:32
字體:
來源:轉載
供稿:網友

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

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

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

起步:用這一種方式理解Promise

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

先看看我們常見的回調模式。doMission1()說:“你要這么做的話,就把doMission2()交給我,我在結束后幫你調用?!彼詴牵?/p>

復制代碼 代碼如下:
doMission1(doMission2);

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

復制代碼 代碼如下:
doMission1().then(doMission2);
 

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

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

復制代碼 代碼如下:
function doMission1(callback){ 
  var value = 1; 
  callback(value); 
}

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

復制代碼 代碼如下:
function doMission1(){ 
  return { 
    then: function(callback){ 
      var value = 1; 
      callback(value); 
    } 
  }; 
}
 

這就完成了轉換。雖然并不是實際有用的轉換,但到這里,其實已經觸及了Promise最為重要的實現要點,即Promise將返回值轉換為帶then方法的對象。

進階:Q的設計路程

從defer開始

design/q0.js是Q初步成型的第一步。它創建了一個名為defer的工具函數,用于創建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的狀態,如果是等待狀態則將回調保存(push),否則立即調用回調。resolve則將肯定這個Promise,更新值的同時運行完所有保存的回調。defer的使用示例如下:

復制代碼 代碼如下:
var oneOneSecondLater = function () { 
  var result = defer(); 
  setTimeout(function () { 
    result.resolve(1); 
  }, 1000); 
  return result; 
};


oneOneSecondLater().then(callback);

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

到此,以上代碼存在一個問題:resolve可以被執行多次。因此,resolve中應該加入對狀態的判斷,保證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.");  } } 

對第二次及更多的調用,可以這樣拋出一個錯誤,也可以直接忽略掉。

分離defer和promise

在前面的實現中,defer生成的對象同時擁有then方法和resolve方法。按照定義,promise關心的是then方法,至于觸發promise改變狀態的resolve,是另一回事。所以,Q接下來將擁有then方法的promise,和擁有resolve的defer分離開來,各自獨立使用。這樣就好像劃清了各自的職責,各自只留一定的權限,這會使代碼邏輯更明晰,易于調整。請看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,你會發現區別很小。一方面,不再拋出錯誤(改為直接忽略第二次及更多的resolve),另一方面,將then方法移動到一個名為promise的對象內。到這里,運行defer()得到的對象(就稱為defer吧),將擁有resolve方法,和一個promise屬性指向另一個對象。這另一個對象就是僅有then方法的promise。這就完成了分離。

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


實現promise的級聯

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

復制代碼 代碼如下:
promise.then(step1).then(step2);

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

then方法必須返回promise。

這個返回的promise必須用傳遞給then方法的回調運行后的返回結果,來設置自己的值。

傳遞給then方法的回調,必須返回一個promise或值。

design/q4.js中,為了實現這一點,新增了一個工具函數ref:

復制代碼 代碼如下:
var ref = function (value) { 
  if (value && typeof value.then === "function") 
    return value; 
  return { 
    then: function (callback) { 
      return ref(callback(value)); 
    } 
  }; 
}; 

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

復制代碼 代碼如下:
ref("step1").then(function(value){ 
  console.log(value); // "step1" 
  return 15; 
}).then(function(value){ 
  console.log(value); // 15 
});

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

design/q4.js通過結合使用這個ref函數,將原來的defer轉變為可級聯的形式:

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包裝的類型,會需要這樣調用。

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


加入錯誤處理

promise的then方法應該可以包含兩個參數,分別是肯定和否定狀態的處理函數(onFulfilled與onRejected)。前面我們實現的promise還只能轉變為肯定狀態,所以,接下來應該加入否定狀態部分。

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

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

它和ref的主要區別是,它返回的對象的then方法,只會取第二個參數的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;   }  }  }; }; 

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

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

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方法的調用結果。而如果像上面這樣返回工具函數reject生成的對象,就會觸發錯誤處理。


融入異步

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

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

復制代碼 代碼如下:
var enqueue = function (callback) { 
  //process.nextTick(callback); // NodeJS 
  setTimeout(callback, 1); // Na?ve browser solution 
};


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

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的部分,都轉變為異步。

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


結語

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

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

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
96精品久久久久中文字幕| 日韩视频第一页| 国产精品久久久久久久久久久新郎| 欧美精品日韩三级| 丝袜亚洲另类欧美重口| 欧美怡春院一区二区三区| 欧美乱大交xxxxx另类电影| 97av视频在线| 亚洲最大成人网色| 日韩高清中文字幕| 久久久久久久激情视频| 久久亚洲电影天堂| 亚洲成色777777女色窝| 成人激情视频网| 亚洲影院色在线观看免费| 亚洲最大中文字幕| 亚洲成年人在线| 亚洲一区二区三区成人在线视频精品| 97精品久久久中文字幕免费| 精品国产成人在线| 国产精品亚洲美女av网站| 成人妇女淫片aaaa视频| 国产精品免费一区| 2020久久国产精品| 91精品国产综合久久男男| 亚洲精品中文字幕女同| 精品少妇v888av| 91在线精品视频| 亚洲精品一区二区久| 国产精品伦子伦免费视频| 日韩在线免费视频| 国产亚洲精品综合一区91| 亚洲欧洲高清在线| 亚洲色图激情小说| 亚洲专区中文字幕| 日韩在线观看电影| 日韩在线免费视频观看| 91九色国产社区在线观看| 欧美黄色三级网站| 国模私拍视频一区| 午夜精品一区二区三区av| 国产精品91视频| 日韩激情av在线免费观看| 国产精品视频白浆免费视频| 欧美精品国产精品日韩精品| 69av成年福利视频| 亚洲偷熟乱区亚洲香蕉av| 欧美xxxx18性欧美| 美日韩精品免费视频| 欧美主播福利视频| 国产免费一区视频观看免费| 亚洲精品自拍第一页| 午夜精品一区二区三区视频免费看| 欧美专区在线播放| 久久久久久久久综合| 国产精品视频区| 91丨九色丨国产在线| 精品国产31久久久久久| 亚洲高清不卡av| 亚洲字幕在线观看| 亚洲第一区中文99精品| 伊是香蕉大人久久| 夜夜嗨av一区二区三区四区| 亚洲精品一区二区在线| 欧美电影免费观看大全| 亚洲欧美一区二区三区在线| 国产一区二区三区视频| 久久全国免费视频| 日韩电影免费观看在线观看| 91精品成人久久| 国产啪精品视频网站| 狠狠久久五月精品中文字幕| 欧美激情精品久久久久久大尺度| 2024亚洲男人天堂| 国产精品自产拍在线观看中文| 日韩精品在线观看一区| 91经典在线视频| 国产视频在线一区二区| 亚洲欧美精品一区| 亚洲精品日韩激情在线电影| 欧美精品久久久久久久久久| 日韩欧美aⅴ综合网站发布| 欧美亚洲激情视频| 国产精品福利在线观看| 日韩在线视频中文字幕| 亚洲成人av中文字幕| 亚洲精品国精品久久99热| 欧美福利视频在线观看| 亚洲伊人第一页| 成人福利网站在线观看| 精品久久久久久亚洲国产300| 国产精品影片在线观看| 日韩精品视频中文在线观看| 久久久久久久久久久久久久久久久久av| 成人动漫网站在线观看| 国产精品一区二区3区| 国产女同一区二区| 亚洲精品美女在线观看| 亚洲一区第一页| 国产精品一区=区| 2019亚洲日韩新视频| 96精品视频在线| 欧美精品久久久久a| 亚洲精品一区二区久| 91伊人影院在线播放| 欧美性猛交xxxxx水多| 日韩在线中文字幕| 国产精品视频一区国模私拍| 亚洲精品国精品久久99热一| 97香蕉超级碰碰久久免费软件| 久久成人精品一区二区三区| 成人免费看片视频| 亚洲 日韩 国产第一| 96sao精品视频在线观看| 国产综合色香蕉精品| 一区二区欧美日韩视频| 国产综合在线视频| 日韩视频免费中文字幕| 成人黄色av播放免费| 久久av资源网站| 国产福利视频一区| 亚洲欧美在线看| 久久久女人电视剧免费播放下载| 国产成人avxxxxx在线看| 69av视频在线播放| 亚洲国产精品高清久久久| 亚洲美女福利视频网站| 久久中文字幕视频| 久久久免费在线观看| 另类图片亚洲另类| 国产精品视频免费观看www| 亚洲第一区中文99精品| 国产aⅴ夜夜欢一区二区三区| 69影院欧美专区视频| 国产精品欧美亚洲777777| 欧美wwwwww| 97久久精品人搡人人玩| 98精品国产高清在线xxxx天堂| 日韩欧美极品在线观看| 久久免费视频在线| 午夜精品视频网站| 亚洲国语精品自产拍在线观看| 欧美丰满老妇厨房牲生活| 欧美成人一区二区三区电影| 亚洲理论在线a中文字幕| 97超级碰碰碰久久久| 亚洲人成电影网| 国产99久久精品一区二区 夜夜躁日日躁| 欧美黑人性生活视频| 国产精品国产三级国产aⅴ9色| 久久福利网址导航| 亚洲国产精久久久久久久| 国产亚洲精品一区二区| 热re99久久精品国产66热| 国产精品大陆在线观看| 91亚洲精品在线观看| 久久久av亚洲男天堂| 狠狠爱在线视频一区| 美女福利精品视频| www国产精品视频| 欧美中文字幕在线| 国产丝袜一区二区三区| 91热福利电影| 国自在线精品视频|