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

異步JavaScript編程中的Promise使用方法

 更新時(shí)間:2015年07月28日 15:46:54   作者:梁砫  
這篇文章主要介紹了異步JavaScript編程中的Promise使用方法,包含Ajax的結(jié)合操作等問題,需要的朋友可以參考下


異步?

我在很多地方都看到過異步(Asynchronous)這個(gè)詞,但在我還不是很理解這個(gè)概念的時(shí)候,卻發(fā)現(xiàn)自己常常會(huì)被當(dāng)做“已經(jīng)很清楚”(* ̄? ̄)。

如果你也有類似的情況,沒關(guān)系,搜索一下這個(gè)詞,就可以得到大致的說明。在這里,我會(huì)對(duì)JavaScript的異步做一點(diǎn)額外解釋。

看一下這段代碼:

var start = new Date();
setTimeout(function(){
  var end = new Date();
  console.log("Time elapsed: ", end - start, "ms");
}, 500);
while (new Date - start < 1000) {};

這段代碼運(yùn)行后會(huì)得到類似Time elapsed: 1013ms這樣的結(jié)果。 setTimeout()所設(shè)定的在未來500ms時(shí)執(zhí)行的函數(shù),實(shí)際等了比1000ms更多的時(shí)間后才執(zhí)行。

要如何解釋呢?調(diào)用setTimeout()時(shí),一個(gè)延時(shí)事件被排入隊(duì)列。然后,繼續(xù)執(zhí)行這之后的代碼,以及更后邊的代碼,直到?jīng)]有任何代碼。沒有任何代碼后,JavaScript線程進(jìn)入空閑,此時(shí)JavaScript執(zhí)行引擎才去翻看隊(duì)列,在隊(duì)列中找到“應(yīng)該觸發(fā)”的事件,然后調(diào)用這個(gè)事件的處理器(函數(shù))。處理器執(zhí)行完成后,又再返回到隊(duì)列,然后查看下一個(gè)事件。

單線程的JavaScript,就是這樣通過隊(duì)列,以事件循環(huán)的形式工作的。所以,前面的代碼中,是用while將執(zhí)行引擎拖在代碼運(yùn)行期間長達(dá)1000ms,而在全部代碼運(yùn)行完回到隊(duì)列前,任何事件都不會(huì)觸發(fā)。這就是JavaScript的異步機(jī)制。
JavaScript的異步難題

JavaScript中的異步操作可能不總是簡單易行的。

Ajax也許是我們用得最多的異步操作。以jQuery為例,發(fā)起一個(gè)Ajax請(qǐng)求的代碼一般是這樣的:

// Ajax請(qǐng)求示意代碼
$.ajax({
  url: url,
  data: dataObject,
  success: function(){},
  error: function(){}
});

這樣的寫法有什么問題嗎?簡單來說,不夠輕便。為什么一定要在發(fā)起請(qǐng)求的地方,就要把success和error這些回調(diào)給寫好呢?假如我的回調(diào)要做很多很多的事情,是要我想起一件事情就跑回這里添加代碼嗎?

再比如,我們要完成這樣一件事:有4個(gè)供Ajax訪問的url地址,需要先Ajax訪問第1個(gè),在第1個(gè)訪問完成后,用拿到的返回?cái)?shù)據(jù)作為參數(shù)再訪問第2個(gè),第2個(gè)訪問完成后再第3個(gè)...以此到4個(gè)全部訪問完成。按照這樣的寫法,似乎會(huì)變成這樣:

$.ajax({
  url: url1,
  success: function(data){
    $.ajax({
      url: url2,
      data: data,
      success: function(data){
        $.ajax({
          //...
        });
      }  
    });
  }
})

你一定會(huì)覺得這種稱為Pyramid of Doom(金字塔厄運(yùn))的代碼看起來很糟糕。習(xí)慣了直接附加回調(diào)的寫法,就可能會(huì)對(duì)這種一個(gè)傳遞到下一個(gè)的異步事件感到無從入手。為這些回調(diào)函數(shù)分別命名并分離存放可以在形式上減少嵌套,使代碼清晰,但仍然不能解決問題。

另一個(gè)常見的難點(diǎn)是,同時(shí)發(fā)送兩個(gè)Ajax請(qǐng)求,然后要在兩個(gè)請(qǐng)求都成功返回后再做一件接下來的事,想一想如果只按前面的方式在各自的調(diào)用位置去附加回調(diào),這是不是好像也有點(diǎn)難辦?

適于應(yīng)對(duì)這些異步操作,可以讓你寫出更優(yōu)雅代碼的就是Promise。
Promise上場

Promise是什么呢?先繼續(xù)以前面jQuery的Ajax請(qǐng)求示意代碼為例,那段代碼其實(shí)可以寫成這個(gè)樣子:

var promise = $.ajax({
  url: url,
  data: dataObject
});
promise.done(function(){});
promise.fail(function(){});

這和前面的Ajax請(qǐng)求示意代碼是等效的??梢钥吹?,Promise的加入使得代碼形式發(fā)生了變化。Ajax請(qǐng)求就好像變量賦值一樣,被“保存”了起來。這就是封裝,封裝將真正意義上讓異步事件變得容易起來。
封裝是有用的

Promise對(duì)象就像是一個(gè)封裝好的對(duì)異步事件的引用。想要在這個(gè)異步事件完成后做點(diǎn)事情?給它附加回調(diào)就可以了,不管附加多少個(gè)也沒問題!

jQuery的Ajax方法會(huì)返回一個(gè)Promise對(duì)象(這是jQuery1.5重點(diǎn)增加的特性)。如果我有do1()、do2()兩個(gè)函數(shù)要在異步事件成功完成后執(zhí)行,只需要這樣做:

promise.done(do1);
// Other code here.
promise.done(do2);

這樣可要自由多了,我只要保存這個(gè)Promise對(duì)象,就在寫代碼的任何時(shí)候,給它附加任意數(shù)量的回調(diào),而不用管這個(gè)異步事件是在哪里發(fā)起的。這就是Promise的優(yōu)勢。
正式的介紹

Promise應(yīng)對(duì)異步操作是如此有用,以至于發(fā)展為了CommonJS的一個(gè)規(guī)范,叫做Promises/A。Promise代表的是某一操作結(jié)束后的返回值,它有3種狀態(tài):

  1.     肯定(fulfilled或resolved),表明該P(yáng)romise的操作成功了。
  2.     否定(rejected或failed),表明該P(yáng)romise的操作失敗了。
  3.     等待(pending),還沒有得到肯定或者否定的結(jié)果,進(jìn)行中。

此外,還有1種名義上的狀態(tài)用來表示Promise的操作已經(jīng)成功或失敗,也就是肯定和否定狀態(tài)的集合,叫做結(jié)束(settled)。Promise還具有以下重要的特性:

  •     一個(gè)Promise只能從等待狀態(tài)轉(zhuǎn)變?yōu)榭隙ɑ蚍穸顟B(tài)一次,一旦轉(zhuǎn)變?yōu)榭隙ɑ蚍穸顟B(tài),就再也不會(huì)改變狀態(tài)。
  •     如果在一個(gè)Promise結(jié)束(成功或失敗,同前面的說明)后,添加針對(duì)成功或失敗的回調(diào),則回調(diào)函數(shù)會(huì)立即執(zhí)行。

想想Ajax操作,發(fā)起一個(gè)請(qǐng)求后,等待著,然后成功收到返回或出現(xiàn)錯(cuò)誤(失?。?。這是否和Promise相當(dāng)一致?

進(jìn)一步解釋Promise的特性還有一個(gè)很好的例子:jQuery的$(document).ready(onReady)。其中onReady回調(diào)函數(shù)會(huì)在DOM就緒后執(zhí)行,但有趣的是,如果在執(zhí)行到這句代碼之前,DOM就已經(jīng)就緒了,那么onReady會(huì)立即執(zhí)行,沒有任何延遲(也就是說,是同步的)。
Promise示例
生成Promise

Promises/A里列出了一系列實(shí)現(xiàn)了Promise的JavaScript庫,jQuery也在其中。下面是用jQuery生成Promise的代碼:

var deferred = $.Deferred();
deferred.done(function(message){console.log("Done: " + message)});
deferred.resolve("morin"); // Done: morin

jQuery自己特意定義了名為Deferred的類,它實(shí)際上就是Promise。$.Deferred()方法會(huì)返回一個(gè)新生成的Promise實(shí)例。一方面,使用deferred.done()、deferred.fail()等為它附加回調(diào),另一方面,調(diào)用deferred.resolve()或deferred.reject()來肯定或否定這個(gè)Promise,且可以向回調(diào)傳遞任意數(shù)據(jù)。
合并Promise

還記得我前文說的同時(shí)發(fā)送2個(gè)Ajax請(qǐng)求的難題嗎?繼續(xù)以jQuery為例,Promise將可以這樣解決它:

var promise1 = $.ajax(url1),
promise2 = $.ajax(url2),
promiseCombined = $.when(promise1, promise2);
promiseCombined.done(onDone);

$.when()方法可以合并多個(gè)Promise得到一個(gè)新的Promise,相當(dāng)于在原多個(gè)Promise之間建立了AND(邏輯與)的關(guān)系,如果所有組成Promise都已成功,則令合并后的Promise也成功,如果有任意一個(gè)組成Promise失敗,則立即令合并后的Promise失敗。
級(jí)聯(lián)Promise

再繼續(xù)我前文的依次執(zhí)行一系列異步任務(wù)的問題。它將用到Promise最為重要的.then()方法(在Promises/A規(guī)范中,也是用“有then()方法的對(duì)象”來定義Promise的)。代碼如下:

var promise = $.ajax(url1);
promise = promise.then(function(data){
  return $.ajax(url2, data);
});
promise = promise.then(function(data){
  return $.ajax(url3, data);
});
// ...

Promise的.then()方法的完整形式是.then(onDone, onFail, onProgress),這樣看上去,它像是一個(gè)一次性就可以把各種回調(diào)都附加上去的簡便方法(.done()、.fail()可以不用了)。沒錯(cuò),你的確可以這樣使用,這是等效的。

但.then()方法還有它更為有用的功能。如同then這個(gè)單詞本身的意義那樣,它用來清晰地指明異步事件的前后關(guān)系:“先這個(gè),然后(then)再那個(gè)”。這稱為Promise的級(jí)聯(lián)。

要級(jí)聯(lián)Promise,需要注意的是,在傳遞給then()的回調(diào)函數(shù)中,一定要返回你想要的代表下一步任務(wù)的Promise(如上面代碼的$.ajax(url2, data))。這樣,前面被賦值的那個(gè)變量才會(huì)變成新的Promise。而如果then()的回調(diào)函數(shù)返回的不是Promise,則then()方法會(huì)返回最初的那個(gè)Promise。

應(yīng)該會(huì)覺得有些難理解?從代碼執(zhí)行的角度上說,上面這段帶有多個(gè)then()的代碼其實(shí)還是被JavaScript引擎運(yùn)行一遍就結(jié)束。但它就像是寫好的舞臺(tái)劇的劇本一樣,讀過一遍后,JavaScript引擎就會(huì)在未來的時(shí)刻,依次安排演員按照劇本來演出,而演出都是異步的。then()方法就是讓你能寫出異步劇本的筆。
將Promise用在基于回調(diào)函數(shù)的API

前文反復(fù)用到的$.ajax()方法會(huì)返回一個(gè)Promise對(duì)象,這其實(shí)只是jQuery特意提供的福利。實(shí)際情況是,大多數(shù)JavaScript API,包括Node.js中的原生函數(shù),都基于回調(diào)函數(shù),而不是基于Promise。這種情況下使用Promise會(huì)需要自行做一些加工。

這個(gè)加工其實(shí)比較簡單和直接,下面是例子:

var deferred = $.Deferred();
setTimeout(deferred.resolve, 1000);
deferred.done(onDone);

這樣,將Promise的肯定或否定的觸發(fā)器,作為API的回調(diào)傳入,就變成了Promise的處理模式了。
Promise是怎么實(shí)現(xiàn)出來的?

本文寫Promise寫到這里,你發(fā)現(xiàn)了全都是基于已有的實(shí)現(xiàn)了Promise的庫。那么,如果要自行構(gòu)筑一個(gè)Promise的話呢?

位列于Promises/A的庫列表第一位的Q可以算是最符合Promises/A規(guī)范且相當(dāng)直觀的實(shí)現(xiàn)。如果你想了解如何做出一個(gè)Promise,可以參考Q提供的設(shè)計(jì)模式解析。

限于篇幅,本文只介紹Promise的應(yīng)用。我會(huì)在以后單獨(dú)開一篇文章來詳述Promise的實(shí)現(xiàn)細(xì)節(jié)。

作為JavaScript后續(xù)版本的ECMAScript 6將原生提供Promise,如果你想知道它的用法,推薦閱讀JavaScript Promises: There and back again。
結(jié)語

Promise這個(gè)詞頑強(qiáng)到不適合翻譯,一眼之下都會(huì)覺得意義不明。不過,在JavaScript里做比較復(fù)雜的異步任務(wù)時(shí),它的確可以提供相當(dāng)多的幫助。

相關(guān)文章

  • 開箱即用的Node.js+Mysql模塊封裝實(shí)現(xiàn)詳解

    開箱即用的Node.js+Mysql模塊封裝實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了開箱即用的Node.js+Mysql模塊封裝實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • node.js中的buffer.toString方法使用說明

    node.js中的buffer.toString方法使用說明

    這篇文章主要介紹了node.js中的buffer.toString方法使用說明,本文介紹了buffer.toString的方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • 如何在Node和瀏覽器控制臺(tái)中打印彩色文字

    如何在Node和瀏覽器控制臺(tái)中打印彩色文字

    這篇文章主要介紹了如何在Node和瀏覽器控制臺(tái)中打印彩色文字,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • 深入剖析Node.js cluster模塊

    深入剖析Node.js cluster模塊

    Node的單線程設(shè)計(jì)已經(jīng)沒法更充分的"壓榨"機(jī)器性能了,Node新增了一個(gè)內(nèi)置模塊cluster,它可以通過一個(gè)父進(jìn)程管理一坨子進(jìn)程的方式來實(shí)現(xiàn)集群的功能,這篇文章主要介紹了深入剖析Node.js cluster模塊,感興趣的小伙伴們可以參考一下
    2018-05-05
  • 基于node.js express mvc輕量級(jí)框架實(shí)踐

    基于node.js express mvc輕量級(jí)框架實(shí)踐

    下面小編就為大家?guī)硪黄趎ode.js express mvc輕量級(jí)框架實(shí)踐。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-09-09
  • Node.js?Webpack常見的模式詳解

    Node.js?Webpack常見的模式詳解

    這篇文章主要介紹了Node.js?Webpack常見的模式,Webpack的另一個(gè)核心是Plugin?,Plugin是可以用于執(zhí)行更加廣泛的任務(wù)如打包優(yōu)化資源管理?環(huán)境變量注入等,需要的朋友可以參考下
    2022-10-10
  • 垃圾回收器的相關(guān)知識(shí)點(diǎn)總結(jié)

    垃圾回收器的相關(guān)知識(shí)點(diǎn)總結(jié)

    本文是小編在網(wǎng)絡(luò)上整理的關(guān)于垃圾回收器的相關(guān)知識(shí)點(diǎn),很多語言和程序都用的到,有興趣的可以學(xué)習(xí)下。
    2018-05-05
  • node.js中的fs.readFileSync方法使用說明

    node.js中的fs.readFileSync方法使用說明

    這篇文章主要介紹了node.js中的fs.readFileSync方法使用說明,本文介紹了fs.readFileSync的方法說明、語法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • Node Puppeteer圖像識(shí)別實(shí)現(xiàn)百度指數(shù)爬蟲的示例

    Node Puppeteer圖像識(shí)別實(shí)現(xiàn)百度指數(shù)爬蟲的示例

    本篇文章主要介紹了Node Puppeteer圖像識(shí)別實(shí)現(xiàn)百度指數(shù)爬蟲的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • 詳解使用抽象語法樹AST實(shí)現(xiàn)一個(gè)AOP切面邏輯

    詳解使用抽象語法樹AST實(shí)現(xiàn)一個(gè)AOP切面邏輯

    這篇文章主要為大家介紹了使用抽象語法樹AST實(shí)現(xiàn)一個(gè)AOP切面邏輯的簡單方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04

最新評(píng)論