許多的語言,為了將非同步模式處理得更像平常的順序,都包含一種有趣的方案庫,它們被稱之為promises,deferreds,或futures。 JavaScript的promises ,可以促進關注點分離,以取代緊密耦合的介面。 本文講的是基於Promises/A 標準的JavaScript promises。 [http://wiki.commonjs.org/wiki/Promises/A]
Promise的用例:
- 執行規則
- 多重遠端驗證
- 逾時處理
- 遠端資料請求
- 動畫
- 將事件邏輯從應用邏輯解耦
- 消除回呼函數的恐怖三角形
- 控制並行的非同步操作
JavaScript promise是一個承諾將在未來傳回值的物件。是具有良好定義的行為的資料對象。 promise有三種可能的狀態:
- Pending(待定)
- Rejected(拒絕)
- Resolved(完成)
一個已經拒絕或完成的承諾屬於已經解決的。一個承諾只能從待定狀態變成已經解決的狀態。之後,承諾的狀態就不變了。承諾可以在它對應的處理完成之後很久還存在。也就是說,我們可以多次取得處理結果。我們透過呼叫promise.then()來取得結果,這個函數一直到承諾對應的處理結束才會回傳。我們可以靈活的串連起一堆承諾。這些串連起來的「then」函數應該回傳一個新的承諾或是最早的那個承諾。
透過這個樣式,我們可以像寫同步程式碼一樣來寫非同步程式碼。主要是透過組合承諾來實現:
- 堆疊式任務:多處散落在程式碼中的,對應同一個承諾。
- 並行任務:多重承諾返回同一個承諾。
- 串列任務:一個承諾,然後接著執行另一個承諾。
- 上面幾種的組合。
為什麼要這麼麻煩?只用基本的回呼函數不行嗎?
回呼函數的問題
回呼函數適合簡單的重複事件,例如根據點擊來讓一個表單有效,或儲存一個REST呼叫的結果。回呼函數也會使程式碼形成一個鏈,一個回呼函數呼叫一個REST函數,並為REST函數設定一個新的回呼函數,這個新的回呼函數再呼叫另一個REST函數,依此類推。程式碼的橫向增長大於縱向的增長。回呼函數看起來很簡單,直到我們需要一個結果,而且是立刻就要,馬上就用在下一行的計算中。
'use strict'; var i = 0; function log(data) {console.log('%d %s', ++i, data); }; function validate() { log("Wait for it ..."); // Sequence of four Long-running async activities setTimeout(function () { log('result first'); setTimeout(function () { log('result second'); setTimeout(function () { log('result third'); setTimeout(function () { log('result fourth') }, 1000); }, 1000); }, 1000); }, 1000); }; validate();
我使用timeout來模擬非同步操作。管理異常的方法是痛苦的,很容易玩漏下游行為。當我們編寫回調,那麼程式碼組織變得混亂。圖2顯示了一個模擬驗證流可以運行在NodeJS REPL。在下一節,我們將從pyramid-of-doom模式遷移到一個連續的promise。
Figure
'use strict'; var i = 0; function log(data) {console.log('%d %s', ++i, data); }; // Asynchronous fn executes a callback result fn function async(arg, callBack) { setTimeout(function(){ log('result ' + arg); callBack(); }, 1000); }; function validate() { log("Wait for it ..."); // Sequence of four Long-running async activities async('first', function () { async('second',function () { async('third', function () { async('fourth', function () {}); }); }); }); }; validate();
在NodeJS REPL執行的結果
$ node scripts/examp2b.js 1 Wait for it ... 2 result first 3 result second 4 result third 5 result fourth $
我曾經遇到一個AngularJS動態驗證的情況,根據對應表的值,動態的限製表單項目的值。限制項的有效值範圍被定義在REST服務上。
我寫了一個調度器,根據請求的值,去操作函數棧,以避免回調嵌套。調度器從堆疊中彈出函數並執行。函數的回調會在結束時重新呼叫調度器,直到堆疊被清空。每次回調都記錄所有從遠端驗證呼叫傳回的驗證錯誤。
我認為我寫的玩意兒是一種反模式。如果我用Angular的$http呼叫提供的promise,在整個驗證過程中我的思維會更近似線性形式,就像同步程式設計。平展的promise鍊是可讀的。繼續...
使用Promises
其中採用了kew promise函式庫。 Q庫同樣適用。要使用該函式庫,請先使用npm將kew函式庫匯入到NodeJS,然後載入程式碼到NodeJS REPL。
Figure
'use strict'; var Q = require('kew'); var i = 0; function log(data) {console.log('%d %s', ++i, data); }; // Asynchronous fn returns a promise function async(arg) { var deferred = Q.defer(); setTimeout(function () { deferred.resolve('result ' + arg);\ }, 1000); return deferred.promise; }; // Flattened promise chain function validate() { log("Wait for it ..."); async('first').then(function(resp){ log(resp); return async('second'); }) .then(function(resp){ log(resp); return async('third') }) .then(function(resp){ log(resp); return async('fourth'); }) .then(function(resp){ log(resp); }).fail(log); }; validate();
輸出和使用巢狀回呼時相同:
$ node scripts/examp2-pflat.js 1 Wait for it ... 2 result first 3 result second 4 result third 5 result fourth $
该代码稍微“长高”了,但我认为更易于理解和修改。更易于加上适当的错误处理。在链的末尾调用fail用于捕获链中错误,但我也可以在任何一个then里面提供一个reject的处理函数做相应的处理。
服务器 或 浏览器
Promises在浏览器中就像在NodeJS服务器中一样有效。下面的地址, http://jsfiddle.net/mauget/DnQDx/,指向JSFiddle的一个展示如何使用一个promise的web页面。 JSFiddle所有的代码是可修改的。我故意操作随意动作。你可以试几次得到相反的结果。它是可以直接扩展到多个promise链, 就像前面NodeJS例子。
并行 Promises
考虑一个异步操作喂养另一个异步操作。让后者包括三个并行异步行为,反过来,喂最后一个行动。只有当所有平行的子请求通过才能通过。这是灵感来自偶遇一打MongoDB操作。有些是合格的并行操作。我实现了promises的流流程图。
我们怎么会模拟那些在该图中心行的并行promises?关键是,最大的promise库有一个全功能,它产生一个包含一组子promises的父promie。当所有的子promises通过,父promise通过。如果有一个子promise拒绝,父promise拒绝。
让十个并行的promises每个都包含一个文字promise。只有当十个子类通过或如果任何子类拒绝,最后的then方法才能完成。
Figure
var promiseVals = ['To ', 'be, ', 'or ', 'not ', 'to ', 'be, ', 'that ', 'is ', 'the ', 'question.']; var startParallelActions = function (){ var promises = []; // Make an asynchronous action from each literal promiseVals.forEach(function(value){ promises.push(makeAPromise(value)); }); // Consolidate all promises into a promise of promises return Q.all(promises); }; startParallelActions ().then( . . .
下面的地址, http://jsfiddle.net/mauget/XKCy2/,针对JSFiddle在浏览器中运行十个并行promises,随机的拒绝或通过。这里有完整的代码用于检查和变化if条件。重新运行,直到你得到一个相反的完成。
孕育 Promise
许多api返回的promise都有一个then函数——他们是thenable。通常我只是通过then处理thenable函数的结果。然而,$q,mpromise,和kew库拥有同样的API用于创建,拒绝,或者通过promise。这里有API文档链接到每个库的引用部分。我通常不需要构造一个promise,除了本文中的包装promise的未知描述和timeout函数。请参考哪些我创建的promises。
Promise库互操作
大多数JavaScript promise库在then级别进行互操作。你可以从一个外部的promise创建一个promise,因为promise可以包装任何类型的值。then可以支持跨库工作。除了then,其他的promise函数则可能不同。如果你需要一个你的库不包含的函数,你可以将一个基于你的库的promise包装到一个新的,基于含有你所需函数的库创建的promise里面。例如,JQuery的promise有时为人所诟病。那么你可以将其包装到Q,$q,mpromise,或者kew库的promise中进行操作。
结语
现在我写了这篇文章,而一年前我却是犹豫要不要拥抱promise的那个。我只是单纯地想完成一项工作。 我不想学习新的API,或是打破我原来的代码(因为误解了promise)。我曾经如此错误地认为!当我下了一点注时,就轻易就赢得了可喜的成果。
在这篇文章中,我已经简单给出了一个单一的promise,promise链,和一个并行的promise的promise的的例子。 Promises不难使用。如果我可以使用它们,任何人都可以。 要查看完整的概念,我支持你点击专家写的参考指南。从Promises/A 的参考开始,从事实上的标准JavaScript的Promise 开始。

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Linux新版
SublimeText3 Linux最新版

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Dreamweaver Mac版
視覺化網頁開發工具

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中