首頁 web前端 js教程 非同步JavaScript程式設計中的Promise使用方法_node.js?1.1.5

非同步JavaScript程式設計中的Promise使用方法_node.js?1.1.5

May 16, 2016 pm 03:48 PM
javascript promise


非同步?

我在很多地方都看過異步(Asynchronous)這個詞,但在我還不是很理解這個概念的時候,卻發現自己常常會被當做「已經很清楚」(*  ̄? ̄)。

如果你也有類似的情況,沒關係,搜尋一下這個詞,就可以得到大致的說明。在這裡,我會對JavaScript的非同步做一點額外解釋。

看一下這段程式碼:

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

這段程式碼運作後會得到類似Time elapsed: 1013ms這樣的結果。 setTimeout()所設定的在未來500ms時執行的函數,實際上等了比1000ms更多的時間後才執行。

要如何解釋呢?當呼叫setTimeout()時,一個延時事件被排入佇列。然後,繼續執行這之後的程式碼,以及更後邊的程式碼,直到沒有任何程式碼。沒有任何程式碼後,JavaScript執行緒進入空閒,此時JavaScript執行引擎才去翻看佇列,在佇列中找到「應該觸發」的事件,然後呼叫這個事件的處理器(函數)。處理器執行完成後,再返回佇列,然後查看下一個事件。

單執行緒的JavaScript,就是這樣透過佇列,以事件循環的形式工作的。所以,前面的程式碼中,是用while將執行引擎拖在程式碼運行期間長達1000ms,而在全部程式碼運行完回到佇列前,任何事件都不會觸發。這就是JavaScript的非同步機制。
JavaScript的非同步難題

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

Ajax也許是我們用得最多的非同步操作。以jQuery為例,啟動一個Ajax請求的程式碼一般是這樣的:

// Ajax请求示意代码
$.ajax({
  url: url,
  data: dataObject,
  success: function(){},
  error: function(){}
});
登入後複製

這樣的寫法有什麼問題嗎?簡單來說,不夠輕便。為什麼一定要在發起請求的地方,就要把success和error這些回呼給寫好呢?假如我的回調要做很多很多的事情,是要我想起一件事情就跑回這裡加碼嗎?

再比如,我們要完成這樣一件事:有4個供Ajax訪問的url地址,需要先Ajax訪問第1個,在第1個訪問完成後,用拿到的返回數據作為參數再訪問第2個,第2個訪問完成後再第3個...以此到4個全部訪問完成。依照這樣的寫法,似乎會變成這樣:

$.ajax({
  url: url1,
  success: function(data){
    $.ajax({
      url: url2,
      data: data,
      success: function(data){
        $.ajax({
          //...
        });
      }  
    });
  }
})
登入後複製

你一定會覺得這種稱為Pyramid of Doom(金字塔厄運)的程式碼看起來很糟。習慣了直接附加回呼的寫法,就可能會對這種一個傳遞到下一個的非同步事件感到無從入手。為這些回調函數分別命名並分離存放可以在形式上減少嵌套,使程式碼清晰,但仍然無法解決問題。

另一個常見的困難是,同時發送兩個Ajax請求,然後要在兩個請求都成功返回後再做一件接下來的事,想一想如果只按前面的方式在各自的呼叫位置去附加回調,這是不是好像也有點難辦?

適於應付這些非同步操作,可以讓你寫出更優雅程式碼的就是Promise。
Promise上場

Promise是什麼呢?先繼續以前面jQuery的Ajax請求示意程式碼為例,那段程式碼其實可以寫成這個樣子:

var promise = $.ajax({
  url: url,
  data: dataObject
});
promise.done(function(){});
promise.fail(function(){});
登入後複製

這和前面的Ajax請求示意代碼是等效的。可以看到,Promise的加入使得程式碼形式改變了。 Ajax請求就好像變數賦值一樣,被「保存」了起來。這就是封裝,封裝將真正意義上讓非同步事件變得容易起來。
封裝是有用的

Promise物件就像是一個封裝好的對非同步事件的引用。想要在這個非同步事件完成後做點事情?給它附加回呼就可以了,不管附加多少個也沒問題!

jQuery的Ajax方法會傳回一個Promise物件(這是jQuery1.5重點增加的特性)。如果我有do1()、do2()兩個函數要在非同步事件成功完成後執行,只需要這樣做:

promise.done(do1);
// Other code here.
promise.done(do2);
登入後複製

這樣可要自由多了,我只要保存這個Promise對象,就在寫代碼的任何時候,給它附加任意數量的回調,而不用管這個非同步事件是在哪裡發起的。這就是Promise的優勢。
正式的介紹

Promise應對非同步操作是如此有用,以至於發展為了CommonJS的一個規範,叫做Promises/A。 Promise代表的是某一作業結束後的回傳值,它有3種狀態:

  1.     肯定(fulfilled或resolved),顯示該Promise的操作成功了。

  2.     否定(rejected或failed),表示該Promise的操作失敗了。

  3.     等待(pending),還沒有得到肯定或否定的結果,進行中。

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

  • 一个Promise只能从等待状态转变为肯定或否定状态一次,一旦转变为肯定或否定状态,就再也不会改变状态。

  • 如果在一个Promise结束(成功或失败,同前面的说明)后,添加针对成功或失败的回调,则回调函数会立即执行。

想想Ajax操作,发起一个请求后,等待着,然后成功收到返回或出现错误(失败)。这是否和Promise相当一致?

进一步解释Promise的特性还有一个很好的例子:jQuery的$(document).ready(onReady)。其中onReady回调函数会在DOM就绪后执行,但有趣的是,如果在执行到这句代码之前,DOM就已经就绪了,那么onReady会立即执行,没有任何延迟(也就是说,是同步的)。
Promise示例
生成Promise

Promises/A里列出了一系列实现了Promise的JavaScript库,jQuery也在其中。下面是用jQuery生成Promise的代码:

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

jQuery自己特意定义了名为Deferred的类,它实际上就是Promise。$.Deferred()方法会返回一个新生成的Promise实例。一方面,使用deferred.done()、deferred.fail()等为它附加回调,另一方面,调用deferred.resolve()或deferred.reject()来肯定或否定这个Promise,且可以向回调传递任意数据。
合并Promise

还记得我前文说的同时发送2个Ajax请求的难题吗?继续以jQuery为例,Promise将可以这样解决它:

var promise1 = $.ajax(url1),
promise2 = $.ajax(url2),
promiseCombined = $.when(promise1, promise2);
promiseCombined.done(onDone);
登入後複製

$.when()方法可以合并多个Promise得到一个新的Promise,相当于在原多个Promise之间建立了AND(逻辑与)的关系,如果所有组成Promise都已成功,则令合并后的Promise也成功,如果有任意一个组成Promise失败,则立即令合并后的Promise失败。
级联Promise

再继续我前文的依次执行一系列异步任务的问题。它将用到Promise最为重要的.then()方法(在Promises/A规范中,也是用“有then()方法的对象”来定义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),这样看上去,它像是一个一次性就可以把各种回调都附加上去的简便方法(.done()、.fail()可以不用了)。没错,你的确可以这样使用,这是等效的。

但.then()方法还有它更为有用的功能。如同then这个单词本身的意义那样,它用来清晰地指明异步事件的前后关系:“先这个,然后(then)再那个”。这称为Promise的级联。

要级联Promise,需要注意的是,在传递给then()的回调函数中,一定要返回你想要的代表下一步任务的Promise(如上面代码的$.ajax(url2, data))。这样,前面被赋值的那个变量才会变成新的Promise。而如果then()的回调函数返回的不是Promise,则then()方法会返回最初的那个Promise。

应该会觉得有些难理解?从代码执行的角度上说,上面这段带有多个then()的代码其实还是被JavaScript引擎运行一遍就结束。但它就像是写好的舞台剧的剧本一样,读过一遍后,JavaScript引擎就会在未来的时刻,依次安排演员按照剧本来演出,而演出都是异步的。then()方法就是让你能写出异步剧本的笔。
将Promise用在基于回调函数的API

前文反复用到的$.ajax()方法会返回一个Promise对象,这其实只是jQuery特意提供的福利。实际情况是,大多数JavaScript API,包括Node.js中的原生函数,都基于回调函数,而不是基于Promise。这种情况下使用Promise会需要自行做一些加工。

这个加工其实比较简单和直接,下面是例子:

var deferred = $.Deferred();
setTimeout(deferred.resolve, 1000);
deferred.done(onDone);
登入後複製

这样,将Promise的肯定或否定的触发器,作为API的回调传入,就变成了Promise的处理模式了。
Promise是怎么实现出来的?

本文写Promise写到这里,你发现了全都是基于已有的实现了Promise的库。那么,如果要自行构筑一个Promise的话呢?

位列于Promises/A的库列表第一位的Q可以算是最符合Promises/A规范且相当直观的实现。如果你想了解如何做出一个Promise,可以参考Q提供的设计模式解析。

限于篇幅,本文只介绍Promise的应用。我会在以后单独开一篇文章来详述Promise的实现细节。

作为JavaScript后续版本的ECMAScript 6将原生提供Promise,如果你想知道它的用法,推荐阅读JavaScript Promises: There and back again。
结语

Promise這個字頑強到不適合翻譯,一眼之下都會覺得意義不明。不過,在JavaScript裡做比較複雜的非同步任務時,它的確可以提供相當多的幫助。

 以上是非同步JavaScript程式設計中的Promise使用方法_node.js?1.1.5的內容,更多相關內容請關注PHP中文網(www.php.cn)!


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

言出必行:兌現承諾的好處和壞處 言出必行:兌現承諾的好處和壞處 Feb 18, 2024 pm 08:06 PM

在日常生活中,我們常常會遇到承諾與兌現之間的問題。無論是在個人關係中,或是在商業交易中,承諾的兌現都是建立信任的關鍵。然而,承諾的利與弊也常常會引起爭議。本文將探討承諾的利與弊,並給予一些建議,如何做到言出必行。承諾的利是顯而易見的。首先,承諾可以建立信任。當一個人信守承諾時,他會讓別人相信自己是個可信賴的人。信任是人與人之間建立的紐帶,它可以讓人們更加

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

深入了解Promise.resolve() 深入了解Promise.resolve() Feb 18, 2024 pm 07:13 PM

Promise.resolve()詳解,需要具體程式碼範例Promise是JavaScript中一種用來處理非同步操作的機制。在實際開發中,常常需要處理一些需要依序執行的非同步任務,而Promise.resolve()方法就是用來傳回一個已經Fulfilled狀態的Promise物件。 Promise.resolve()是Promise類別的靜態方法,它接受一個

See all articles