摘要:對(duì)于的來(lái)說(shuō)基元函數(shù)包括組合函數(shù)的類型簽名返回情況完成如果傳入的可迭代對(duì)象為空,會(huì)同步地返回一個(gè)已完成狀態(tài)的。相反,如果是在指定的時(shí)間之后完成,剛返回結(jié)果就是一個(gè)拒絕狀態(tài)的從而觸發(fā)方法指定的回調(diào)函數(shù)。在行中,對(duì)每個(gè)小任務(wù)得到的結(jié)果進(jìn)行匯總。
為了保證的可讀性,本文采用意譯而非直譯。
想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你!
從ES6 開始,我們大都使用的是 Promise.all()和Promise.race(),Promise.allSettled() 提案已經(jīng)到第4階段,因此將會(huì)成為ECMAScript 2020的一部分。
1.概述Promise.all
Promise.all(iterable) 方法返回一個(gè) Promise 實(shí)例,此實(shí)例在 iterable 參數(shù)內(nèi)所有的 promise 都“完成(resolved)”或參數(shù)中不包含 promise 時(shí)回調(diào)完成(resolve);如果參數(shù)中 promise 有一個(gè)失敗(rejected),此實(shí)例回調(diào)失?。╮eject),失敗原因的是第一個(gè)失敗 promise 的結(jié)果
Promise.race
Promise.race(iterable) 方法返回一個(gè) promise,一旦迭代器中的某個(gè)promise解決或拒絕,返回的 promise就會(huì)解決或拒絕。
Promise.allSettled
Promise.allSettled()方法返回一個(gè)promise,該promise在所有給定的promise已被解析或被拒絕后解析,并且每個(gè)對(duì)象都描述每個(gè)promise的結(jié)果。
回顧: Promise 狀態(tài)給定一個(gè)返回Promise的異步操作,以下這些是Promise的可能狀態(tài):
pending: 初始狀態(tài),既不是成功,也不是失敗狀態(tài)。
fulfilled: 意味著操作成功完成。
rejected: 意味著操作失敗。
Settled: Promise要么被完成,要么被拒絕。Promise一旦達(dá)成,它的狀態(tài)就不再改變。
3.什么是組合又稱部分-整體模式,將對(duì)象整合成樹形結(jié)構(gòu)以表示“部分整體”的層次結(jié)構(gòu)。組合模式使得用戶對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性,它基于兩種函數(shù):
基元函數(shù)(簡(jiǎn)短:基元)創(chuàng)建原子塊。
組合函數(shù)(簡(jiǎn)稱:組合)將原子和/或復(fù)合件組合在一起以形成復(fù)合件。
對(duì)于 JS 的 Promises 來(lái)說(shuō)
基元函數(shù)包括:Promise.resolve()、Promise.reject()
組合函數(shù):Promise.all(), Promise.race(), Promise.allSettled()
4. Promise.all()Promise.all()的類型簽名:
Promise.all
返回情況:
完成(Fulfillment):
如果傳入的可迭代對(duì)象為空,Promise.all 會(huì)同步地返回一個(gè)已完成(resolved)狀態(tài)的promise。
如果所有傳入的 promise 都變?yōu)橥瓿蔂顟B(tài),或者傳入的可迭代對(duì)象內(nèi)沒(méi)有 promise,Promise.all 返回的 promise 異步地變?yōu)橥瓿伞?br>在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個(gè)數(shù)組,它包含所有的傳入迭代參數(shù)對(duì)象的值(也包括非 promise 值)。
失敗/拒絕(Rejection):
如果傳入的 promise 中有一個(gè)失?。?b>rejected),Promise.all 異步地將失敗的那個(gè)結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其它 promise 是否完成。
來(lái)個(gè)例子:
const promises = [ Promise.resolve("a"), Promise.resolve("b"), Promise.resolve("c"), ]; Promise.all(promises) .then((arr) => assert.deepEqual( arr, ["a", "b", "c"] ));
如果其中的一個(gè) promise 被拒絕,那么又是什么情況:
const promises = [ Promise.resolve("a"), Promise.resolve("b"), Promise.reject("ERROR"), ]; Promise.all(promises) .catch((err) => assert.equal( err, "ERROR" ));
下圖說(shuō)明Promise.all()是如何工作的
4.1 異步 .map() 與 Promise.all()數(shù)組轉(zhuǎn)換方法,如.map()、.filter()等,用于同步計(jì)算。例如
function timesTwoSync(x) { return 2 * x; } const arr = [1, 2, 3]; const result = arr.map(timesTwoSync); assert.deepEqual(result, [2, 4, 6]);
如果.map()的回調(diào)是基于Promise的函數(shù)會(huì)發(fā)生什么? 使用這種方式 .map()返回的的結(jié)果是一個(gè)Promises數(shù)組。
Promises數(shù)組不是普通代碼可以使用的數(shù)據(jù),但我們可以通過(guò)Promise.all()來(lái)解決這個(gè)問(wèn)題:它將Promises數(shù)組轉(zhuǎn)換為Promise,并使用一組普通值數(shù)組來(lái)實(shí)現(xiàn)。
function timesTwoAsync(x) { return new Promise(resolve => resolve(x * 2)); } const arr = [1, 2, 3]; const promiseArr = arr.map(timesTwoAsync); Promise.all(promiseArr) .then(result => { assert.deepEqual(result, [2, 4, 6]); });更實(shí)際工作上關(guān)于 .map()示例
接下來(lái),咱們使用.map()和Promise.all()從Web下載文件。 首先,咱們需要以下幫助函數(shù):
function downloadText(url) { return fetch(url) .then((response) => { // (A) if (!response.ok) { // (B) throw new Error(response.statusText); } return response.text(); // (C) }); }
downloadText()使用基于Promise的fetch API 以字符串流的方式下載文件:
首先,它異步檢索響應(yīng)(第A行)。
response.ok(B行)檢查是否存在“找不到文件”等錯(cuò)誤。
如果沒(méi)有錯(cuò)誤,使用.text()(第C行)以字符串的形式取回文件的內(nèi)容。
在下面的示例中,咱們 下載了兩個(gè)文件
const urls = [ "http://example.com/first.txt", "http://example.com/second.txt", ]; const promises = urls.map( url => downloadText(url)); Promise.all(promises) .then( (arr) => assert.deepEqual( arr, ["First!", "Second!"] ));Promise.all()的一個(gè)簡(jiǎn)版實(shí)現(xiàn)
function all(iterable) { return new Promise((resolve, reject) => { let index = 0; for (const promise of iterable) { // Capture the current value of `index` const currentIndex = index; promise.then( (value) => { if (anErrorOccurred) return; result[currentIndex] = value; elementCount++; if (elementCount === result.length) { resolve(result); } }, (err) => { if (anErrorOccurred) return; anErrorOccurred = true; reject(err); }); index++; } if (index === 0) { resolve([]); return; } let elementCount = 0; let anErrorOccurred = false; const result = new Array(index); }); }5. Promise.race()
Promise.race()方法的定義:
Promise.race
Promise.race(iterable) 方法返回一個(gè) promise,一旦迭代器中的某個(gè)promise解決或拒絕,返回的 promise就會(huì)解決或拒絕。來(lái)幾個(gè)例子,瞧瞧:
const promises = [ new Promise((resolve, reject) => setTimeout(() => resolve("result"), 100)), // (A) new Promise((resolve, reject) => setTimeout(() => reject("ERROR"), 200)), // (B) ]; Promise.race(promises) .then((result) => assert.equal( // (C) result, "result"));
在第 A 行,Promise 是完成狀態(tài) ,所以 第 C 行會(huì)執(zhí)行(盡管第 B 行被拒絕)。
如果 Promise 被拒絕首先執(zhí)行,在來(lái)看看情況是嘛樣的:
const promises = [ new Promise((resolve, reject) => setTimeout(() => resolve("result"), 200)), new Promise((resolve, reject) => setTimeout(() => reject("ERROR"), 100)), ]; Promise.race(promises) .then( (result) => assert.fail(), (err) => assert.equal( err, "ERROR"));
注意,由于 Promse 先被拒絕,所以 Promise.race() 返回的是一個(gè)被拒絕的 Promise
這意味著Promise.race([])的結(jié)果永遠(yuǎn)不會(huì)完成。
下圖演示了Promise.race()的工作原理:
Promise.race() 在 Promise 超時(shí)下的情況在本節(jié)中,我們將使用Promise.race()來(lái)處理超時(shí)的 Promise。 以下輔助函數(shù):
function resolveAfter(ms, value=undefined) { return new Promise((resolve, reject) => { setTimeout(() => resolve(value), ms); }); }
resolveAfter() 主要做的是在指定的時(shí)間內(nèi),返回一個(gè)狀態(tài)為 resolve 的 Promise,值為為傳入的 value
調(diào)用上面方法:
function timeout(timeoutInMs, promise) { return Promise.race([ promise, resolveAfter(timeoutInMs, Promise.reject(new Error("Operation timed out"))), ]); }
timeout() 返回一個(gè)Promise,該 Promise 的狀態(tài)取決于傳入 promise 狀態(tài) 。
其中 timeout 函數(shù)中的 resolveAfter(timeoutInMs, Promise.reject(new Error("Operation timed out")) ,通過(guò) resolveAfter 定義可知,該結(jié)果返回的是一個(gè)被拒絕狀態(tài)的 Promise。
再來(lái)看看timeout(timeoutInMs, promise)的運(yùn)行情況。如果傳入promise在指定的時(shí)間之前狀態(tài)為完成時(shí),timeout 返回結(jié)果就是一個(gè)完成狀態(tài)的 Promise,可以通過(guò).then的第一個(gè)回調(diào)參數(shù)處理返回的結(jié)果。
timeout(200, resolveAfter(100, "Result!")) .then(result => assert.equal(result, "Result!"));
相反,如果是在指定的時(shí)間之后完成,剛 timeout 返回結(jié)果就是一個(gè)拒絕狀態(tài)的 Promise,從而觸發(fā)catch方法指定的回調(diào)函數(shù)。
timeout(100, resolveAfter(2000, "Result!")) .catch(err => assert.deepEqual(err, new Error("Operation timed out")));
重要的是要了解“Promise 超時(shí)”的真正含義:
如果傳入入Promise 較到的得到解決,其結(jié)果就會(huì)給返回的 Promise。
如果沒(méi)有足夠快得到解決,輸出的 Promise 的狀態(tài)為拒絕。
也就是說(shuō),超時(shí)只會(huì)阻止傳入的Promise,影響輸出 Promise(因?yàn)镻romise只能解決一次), 但它并沒(méi)有阻止傳入Promise的異步操作。
5.2 Promise.race() 的一個(gè)簡(jiǎn)版實(shí)現(xiàn)以下是 Promise.race()的一個(gè)簡(jiǎn)化實(shí)現(xiàn)(它不執(zhí)行安全檢查)
function race(iterable) { return new Promise((resolve, reject) => { for (const promise of iterable) { promise.then( (value) => { if (settlementOccurred) return; settlementOccurred = true; resolve(value); }, (err) => { if (settlementOccurred) return; settlementOccurred = true; reject(err); }); } let settlementOccurred = false; }); }6.Promise.allSettled()
“Promise.allSettled”這一特性是由Jason Williams,Robert Pamely和Mathias Bynens提出。
promise.allsettle()方法的定義:
Promise.allSettled
: Promise<Array
它返回一個(gè)Array的Promise,其元素具有以下類型特征:
type SettlementObject= FulfillmentObject | RejectionObject; interface FulfillmentObject { status: "fulfilled"; value: T; } interface RejectionObject { status: "rejected"; reason: unknown; }
Promise.allSettled()方法返回一個(gè)promise,該promise在所有給定的promise已被解析或被拒絕后解析,并且每個(gè)對(duì)象都描述每個(gè)promise的結(jié)果。
舉例說(shuō)明, 比如各位用戶在頁(yè)面上面同時(shí)填了3個(gè)獨(dú)立的表單, 這三個(gè)表單分三個(gè)接口提交到后端, 三個(gè)接口獨(dú)立, 沒(méi)有順序依賴, 這個(gè)時(shí)候我們需要等到請(qǐng)求全部完成后給與用戶提示表單提交的情況
在多個(gè)promise同時(shí)進(jìn)行時(shí)咱們很快會(huì)想到使用Promise.all來(lái)進(jìn)行包裝, 但是由于Promise.all的短路特性, 三個(gè)提交中若前面任意一個(gè)提交失敗, 則后面的表單也不會(huì)進(jìn)行提交了, 這就與咱們需求不符合.
Promise.allSettled跟Promise.all類似, 其參數(shù)接受一個(gè)Promise的數(shù)組, 返回一個(gè)新的Promise, 唯一的不同在于, 其不會(huì)進(jìn)行短路, 也就是說(shuō)當(dāng)Promise全部處理完成后我們可以拿到每個(gè)Promise的狀態(tài), 而不管其是否處理成功.
下圖說(shuō)明promise.allsettle()是如何工作的
6.1 Promise.allSettled() 例子這是Promise.allSettled() 使用方式快速演示示例
Promise.allSettled([ Promise.resolve("a"), Promise.reject("b"), ]) .then(arr => assert.deepEqual(arr, [ { status: "fulfilled", value: "a" }, { status: "rejected", reason: "b" }, ]));6.2 Promise.allSettled() 較復(fù)雜點(diǎn)的例子
這個(gè)示例類似于.map()和Promise.all()示例(我們從其中借用了downloadText()函數(shù)):我們下載多個(gè)文本文件,這些文件的url存儲(chǔ)在一個(gè)數(shù)組中。但是,這一次,咱們不希望在出現(xiàn)錯(cuò)誤時(shí)停止,而是希望繼續(xù)執(zhí)行。Promise.allSettled()允許咱們這樣做:
const urls = [ "http://example.com/exists.txt", "http://example.com/missing.txt", ]; const result = Promise.allSettled( urls.map(u => downloadText(u))); result.then( arr => assert.deepEqual( arr, [ { status: "fulfilled", value: "Hello!", }, { status: "rejected", reason: new Error("Not Found"), }, ] ));6.3 Promise.allSettled() 的簡(jiǎn)化實(shí)現(xiàn)
這是promise.allsettle()的簡(jiǎn)化實(shí)現(xiàn)(不執(zhí)行安全檢查)
function allSettled(iterable) { return new Promise((resolve, reject) => { function addElementToResult(i, elem) { result[i] = elem; elementCount++; if (elementCount === result.length) { resolve(result); } } let index = 0; for (const promise of iterable) { // Capture the current value of `index` const currentIndex = index; promise.then( (value) => addElementToResult( currentIndex, { status: "fulfilled", value }), (reason) => addElementToResult( currentIndex, { status: "rejected", reason })); index++; } if (index === 0) { resolve([]); return; } let elementCount = 0; const result = new Array(index); }); }7. 短路特性
Promise.all() 和 romise.race() 都具有 短路特性
Promise.all(): 如果參數(shù)中 promise 有一個(gè)失?。╮ejected),此實(shí)例回調(diào)失?。╮eject)
Promise.race():如果參數(shù)中某個(gè)promise解決或拒絕,返回的 promise就會(huì)解決或拒絕。
8.并發(fā)性和 Promise.all() 8.1 順序執(zhí)行與并發(fā)執(zhí)行考慮下面的代碼:
asyncFunc1() .then(result1 => { assert.equal(result1, "one"); return asyncFunc2(); }) .then(result2 => { assert.equal(result2, "two"); });
使用.then()順序執(zhí)行基于Promise的函數(shù):只有在 asyncFunc1()的結(jié)果被解決后才會(huì)執(zhí)行asyncFunc2() 。
而 Promise.all() 是并發(fā)執(zhí)行的
Promise.all([asyncFunc1(), asyncFunc2()]) .then(arr => { assert.deepEqual(arr, ["one", "two"]); });9.2 并發(fā)技巧:關(guān)注操作何時(shí)開始
確定并發(fā)異步代碼的技巧:關(guān)注異步操作何時(shí)啟動(dòng),而不是如何處理它們的Promises。
例如,下面的每個(gè)函數(shù)都同時(shí)執(zhí)行asyncFunc1()和asyncFunc2(),因?yàn)樗鼈儙缀跬瑫r(shí)啟動(dòng)。
function concurrentAll() { return Promise.all([asyncFunc1(), asyncFunc2()]); } function concurrentThen() { const p1 = asyncFunc1(); const p2 = asyncFunc2(); return p1.then(r1 => p2.then(r2 => [r1, r2])); }
另一方面,以下兩個(gè)函數(shù)依次執(zhí)行asyncFunc1()和asyncFunc2(): asyncFunc2()僅在asyncFunc1()的解決之后才調(diào)用。
function sequentialThen() { return asyncFunc1() .then(r1 => asyncFunc2() .then(r2 => [r1, r2])); } function sequentialAll() { const p1 = asyncFunc1(); const p2 = p1.then(() => asyncFunc2()); return Promise.all([p1, p2]); }9.3 Promise.all() 與 Fork-Join 分治編程
Promise.all() 與并發(fā)模式“fork join”松散相關(guān)。重溫一下咱們前面的一個(gè)例子:
Promise.all([ // (A) fork downloadText("http://example.com/first.txt"), downloadText("http://example.com/second.txt"), ]) // (B) join .then( (arr) => assert.deepEqual( arr, ["First!", "Second!"] ));
Fork:在A行中,分割兩個(gè)異步任務(wù)并同時(shí)執(zhí)行它們。
Join:在B行中,對(duì)每個(gè)小任務(wù)得到的結(jié)果進(jìn)行匯總。
代碼部署后可能存在的BUG沒(méi)法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。
交流干貨系列文章匯總?cè)缦?,覺(jué)得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛(ài)好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/106730.html
摘要:實(shí)例生成以后,用方法分別指定狀態(tài)和狀態(tài)的回調(diào)函數(shù)。則是或的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。上述代碼也可以理解成這樣處理和前一個(gè)回調(diào)函數(shù)運(yùn)行時(shí)發(fā)生的錯(cuò)誤發(fā)生錯(cuò)誤方法用于指定不管對(duì)象最后狀態(tài)如何,都會(huì)執(zhí)行的回調(diào)函數(shù)。 什么是promise? Promise(承諾),在程序中的意思就是承諾我過(guò)一段時(shí)間(通常是一個(gè)異步操作)后會(huì)給你一個(gè)結(jié)果,是異步編程的一種解決方案。從語(yǔ)法上說(shuō),原生Pro...
摘要:舉例來(lái)說(shuō)即便某個(gè)失敗了,也不會(huì)導(dǎo)致的發(fā)生,這樣在不在乎是否有項(xiàng)目失敗,只要拿到都結(jié)束的信號(hào)的場(chǎng)景很有用。對(duì)于則稍有不同只要有子項(xiàng),就會(huì)完成,哪怕第一個(gè)了,而第二個(gè)了,也會(huì),而對(duì)于,這種場(chǎng)景會(huì)直接。 1. 引言 本周精讀的內(nèi)容是:Google I/O 19。 2019 年 Google I/O 介紹了一些激動(dòng)人心的 JS 新特性,這些特性有些已經(jīng)被主流瀏覽器實(shí)現(xiàn),并支持 polyfill...
摘要:最受歡迎的引擎是,由和使用,用于,以及使用的。引擎它們是如何工作的全局執(zhí)行上下文和調(diào)用堆棧剛剛了解了引擎如何讀取變量和函數(shù)聲明,它們最終被放入了全局內(nèi)存堆中。事件循環(huán)只有一個(gè)任務(wù)它檢查調(diào)用堆棧是否為空。 為了保證可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 有沒(méi)有想過(guò)瀏覽器如何讀取和運(yùn)行JS代碼? 這看起來(lái)很神奇,我們可以通過(guò)瀏覽...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。怎么處理每個(gè)引擎都有一個(gè)基本組件,稱為調(diào)用棧。也就是說(shuō),如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過(guò)回調(diào)隊(duì)列。例如方法是在中傳遞的回調(diào)函數(shù)。 ? 翻譯:瘋狂的技術(shù)宅 原文:www.valentinog.com/blog/engine… 從Call Stack,Global Me...
摘要:最受歡迎的引擎是,在和中使用,用于,以及所使用的。單線程的我們說(shuō)是單線程的,因?yàn)橛幸粋€(gè)調(diào)用棧處理我們的函數(shù)。也就是說(shuō),如果有其他函數(shù)等待執(zhí)行,函數(shù)是不能離開調(diào)用棧的。每個(gè)異步函數(shù)在被送入調(diào)用棧之前必須通過(guò)回調(diào)隊(duì)列。 翻譯:瘋狂的技術(shù)宅原文:https://www.valentinog.com/bl... 本文首發(fā)微信公眾號(hào):前端先鋒歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章 sh...
閱讀 710·2021-11-11 16:55
閱讀 2243·2021-11-11 16:55
閱讀 2042·2021-11-11 16:55
閱讀 2412·2021-10-25 09:46
閱讀 1685·2021-09-22 15:20
閱讀 2438·2021-09-10 10:51
閱讀 1790·2021-08-25 09:38
閱讀 2694·2019-08-30 12:48