async function fileSizes(files) {
let lines = files.map(async fileName => {
return fileName + ": " +
(await textFile(fileName)).length;
});
return (await Promise.all(lines)).join("\n");
}
修正后的異步文件大小收集函數(shù)解析
這個(gè)改進(jìn)版的 fileSizes 函數(shù)解決了之前的競(jìng)爭(zhēng)條件問(wèn)題,讓我們分析它的工作原理:
代碼功能與改進(jìn)
async function fileSizes(files) {
// 1. 創(chuàng)建Promise數(shù)組(不立即執(zhí)行拼接)
let lines = files.map(async fileName => {
return fileName + ": " +
(await textFile(fileName)).length; // 返回單個(gè)文件的結(jié)果
});
// 2. 等待所有Promise完成并收集結(jié)果
return (await Promise.all(lines)).join("\n");
// 等價(jià)于:
// const results = await Promise.all(lines);
// return results.join("\n");
}
核心改進(jìn):
-
避免共享狀態(tài)修改:不再直接操作共享的
list變量 - 結(jié)果隔離:每個(gè)異步操作返回獨(dú)立的字符串結(jié)果
-
統(tǒng)一合并:使用
Promise.all等待所有結(jié)果完成后,再一次性拼接
執(zhí)行流程詳解
假設(shè)有三個(gè)文件:
const files = ["a.txt", "b.txt", "c.txt"];
-
生成Promise數(shù)組:
files.map(...)- 為每個(gè)文件創(chuàng)建一個(gè)異步任務(wù)
- 每個(gè)任務(wù)返回一個(gè) Promise,解析為對(duì)應(yīng)文件的結(jié)果字符串
-
并行執(zhí)行異步任務(wù):
await Promise.all(lines)- 并行讀取所有文件
- 等待所有文件讀取完成
- 結(jié)果數(shù)組順序與原始
files數(shù)組一致
-
拼接最終結(jié)果:
(await Promise.all(lines)).join("\n")- 將結(jié)果數(shù)組按原始順序拼接成字符串
- 保證輸出順序與輸入文件順序一致
關(guān)鍵技術(shù)點(diǎn)
-
Promise.all 的順序保證:
- 即使異步任務(wù)完成時(shí)間不同
-
Promise.all返回的結(jié)果數(shù)組順序始終與輸入 Promise 數(shù)組一致
-
避免競(jìng)態(tài)條件:
// 之前的錯(cuò)誤寫法(共享狀態(tài)修改) list += ... // 非原子操作,多任務(wù)沖突 // 現(xiàn)在的正確寫法(獨(dú)立結(jié)果) return ... // 每個(gè)任務(wù)返回獨(dú)立值,無(wú)共享狀態(tài) -
異步映射與聚合:
files.map(async ...) // 映射為Promise數(shù)組 Promise.all(...) // 聚合所有結(jié)果 results.join("\n") // 按序拼接
烏鴉的異步智慧應(yīng)用
在卡拉的LED項(xiàng)目中,類似的模式可以用于并行控制多個(gè)屏幕:
async function updateAllScreens(frames) {
// 1. 為每個(gè)屏幕創(chuàng)建更新任務(wù)
const updateTasks = screenAddresses.map((addr, index) => {
return sendCommand(addr, "update", frames[index])
.then(() => `${addr} 更新成功`)
.catch(err => `${addr} 更新失敗: ${err.message}`);
});
// 2. 等待所有任務(wù)完成并收集結(jié)果
const results = await Promise.all(updateTasks);
// 3. 生成匯總報(bào)告(順序與屏幕數(shù)組一致)
return results.join("\n");
}
這種設(shè)計(jì)確保了:
- 所有屏幕更新請(qǐng)求并行發(fā)送
- 無(wú)論響應(yīng)順序如何,結(jié)果都按屏幕原始順序排列
- 避免了多個(gè)屏幕更新操作之間的數(shù)據(jù)競(jìng)爭(zhēng)
就像烏鴉同時(shí)處理多個(gè)堅(jiān)果一樣,每個(gè)堅(jiān)果都獨(dú)立處理,最后按順序整齊排列。 ???
這類錯(cuò)誤很容易犯,尤其是在使用 await 時(shí),你應(yīng)當(dāng)留意代碼中存在執(zhí)行間隙的位置。JavaScript 顯式的異步機(jī)制(無(wú)論是通過(guò)回調(diào)、Promise 還是 await)有一個(gè)優(yōu)勢(shì):相對(duì)容易發(fā)現(xiàn)這些間隙的位置。
總結(jié)
異步編程使程序能夠在等待長(zhǎng)時(shí)間操作時(shí)不凍結(jié)整個(gè)系統(tǒng)成為可能。JavaScript 環(huán)境通常使用回調(diào)函數(shù)(即操作完成時(shí)被調(diào)用的函數(shù))來(lái)實(shí)現(xiàn)這種編程風(fēng)格。事件循環(huán)會(huì)在適當(dāng)?shù)臅r(shí)候調(diào)度這些回調(diào)函數(shù)依次執(zhí)行,因此它們的執(zhí)行不會(huì)重疊。
Promise(表示未來(lái)可能完成的操作的對(duì)象)和 async 函數(shù)(允許你像編寫同步程序一樣編寫異步程序)使異步編程變得更加容易。
異步編程的陷阱與核心要點(diǎn)解析
這類錯(cuò)誤很容易犯,尤其是在使用 await 時(shí),你需要清楚代碼中哪些地方存在執(zhí)行間隙。JavaScript 顯式的異步機(jī)制(無(wú)論是通過(guò)回調(diào)、Promise 還是 await)有一個(gè)優(yōu)點(diǎn):相對(duì)容易發(fā)現(xiàn)這些間隙的位置。
核心總結(jié)
1. 異步編程的本質(zhì)
異步編程使得程序在等待耗時(shí)操作時(shí)不會(huì)凍結(jié)整個(gè)系統(tǒng)。JavaScript 環(huán)境通常通過(guò)回調(diào)函數(shù)實(shí)現(xiàn)這種編程模式——回調(diào)函數(shù)會(huì)在操作完成時(shí)被調(diào)用。事件循環(huán)(Event Loop)負(fù)責(zé)調(diào)度這些回調(diào),確保它們按順序執(zhí)行,不會(huì)重疊。
2. 異步機(jī)制的演進(jìn)
- 回調(diào)函數(shù)(Callbacks):傳統(tǒng)異步處理方式,但容易導(dǎo)致“回調(diào)地獄”(Callback Hell)。
-
Promise:用對(duì)象封裝未來(lái)可能完成的操作,通過(guò)
.then()和.catch()處理結(jié)果,避免回調(diào)嵌套。 - async/await:以同步代碼的寫法實(shí)現(xiàn)異步邏輯,使異步流程更直觀,可讀性更高。
3. 關(guān)鍵優(yōu)勢(shì)與注意事項(xiàng)
- 非阻塞特性:異步操作在等待時(shí)釋放主線程,允許程序處理其他事件(如用戶輸入、UI 渲染)。
-
顯式間隙管理:通過(guò) Promise 和
await,可以清晰識(shí)別代碼中的異步間隙,避免共享狀態(tài)導(dǎo)致的競(jìng)態(tài)條件(Race Conditions)。 -
錯(cuò)誤處理:Promise 的
.catch()和try/catch結(jié)合await,能更優(yōu)雅地捕獲異步操作中的異常。
烏鴉的異步哲學(xué)
在卡拉的LED燈光系統(tǒng)中,異步編程是實(shí)現(xiàn)復(fù)雜動(dòng)畫的核心。例如,當(dāng)她需要讓九個(gè)屏幕依次顯示不同的像素圖案時(shí):
async function animateCrowFlight() {
for (const frame of flightFrames) {
// 并行更新所有屏幕(非阻塞)
await Promise.all(screenAddresses.map(addr =>
displayFrameOnScreen(addr, frame[addr])
));
// 等待動(dòng)畫間隔,期間可響應(yīng)其他事件(如緊急停止信號(hào))
await wait(100);
}
}
這里的 await 并非阻塞主線程,而是讓事件循環(huán)在等待時(shí)處理其他任務(wù)(如傳感器輸入)。這種機(jī)制讓卡拉的程序既能實(shí)現(xiàn)精密的動(dòng)畫同步,又能保持對(duì)外部事件的實(shí)時(shí)響應(yīng)——就像烏鴉在飛行中同時(shí)觀察周圍環(huán)境,隨時(shí)調(diào)整方向。
異步編程的智慧,本質(zhì)上是對(duì)“時(shí)間間隙”的掌控。正如JavaScript用事件循環(huán)調(diào)度回調(diào),烏鴉用代碼間隙處理突發(fā)情況,兩者都在無(wú)序中創(chuàng)造出有序的節(jié)奏。 ?????
練習(xí)題:活動(dòng)時(shí)間分析
卡拉實(shí)驗(yàn)室附近有一個(gè)由運(yùn)動(dòng)傳感器激活的安全攝像頭。它連接到網(wǎng)絡(luò),激活時(shí)會(huì)發(fā)送視頻流。為避免被發(fā)現(xiàn),卡拉設(shè)置了一個(gè)系統(tǒng),該系統(tǒng)會(huì)檢測(cè)這種無(wú)線網(wǎng)絡(luò)流量,并在室外有活動(dòng)時(shí)在她的巢穴中點(diǎn)亮一盞燈,以便她知道何時(shí)需要保持安靜。
她還記錄了攝像頭被觸發(fā)的時(shí)間,并希望利用這些信息來(lái)可視化顯示平均一周中哪些時(shí)間段通常安靜,哪些時(shí)間段通常繁忙。日志存儲(chǔ)在文件中,每行包含一個(gè)時(shí)間戳數(shù)字(由 Date.now() 返回)。
1695709940692
1695701068331
1695701189163
camera_logs.txt 文件包含一個(gè)日志文件列表。編寫一個(gè)異步函數(shù) activityTable(day),它接收一周中的某一天作為參數(shù),并返回一個(gè)包含24個(gè)數(shù)字的數(shù)組,數(shù)組中的每個(gè)數(shù)字表示該天對(duì)應(yīng)小時(shí)內(nèi)觀察到的攝像頭網(wǎng)絡(luò)流量次數(shù)。天數(shù)使用 Date.getDay 方法的編號(hào)系統(tǒng)(星期日為0,星期六為6)。
沙箱環(huán)境提供的 activityGraph 函數(shù)可以將這樣的數(shù)組匯總為一個(gè)字符串。
要讀取文件,請(qǐng)使用前面定義的 textFile 函數(shù)——給定一個(gè)文件名,它返回一個(gè)解析為文件內(nèi)容的 Promise。請(qǐng)記住,new Date(timestamp) 會(huì)創(chuàng)建該時(shí)間的 Date 對(duì)象,該對(duì)象具有返回星期幾和小時(shí)的 getDay 和 getHours 方法。
兩種類型的文件——日志文件列表和日志文件本身——都將每條數(shù)據(jù)單獨(dú)放在一行,用換行符(\n)分隔。
async function activityTable(day) {
let logFileList = await textFile("camera_logs.txt");
// Your code here
}
activityTable(1)
.then(table => console.log(activityGraph(table)));
異步活動(dòng)時(shí)間表函數(shù)解析
這個(gè) activityTable 函數(shù)的任務(wù)是統(tǒng)計(jì)特定星期幾(如星期一)每小時(shí)的攝像頭活動(dòng)次數(shù)。讓我們逐步解析如何實(shí)現(xiàn)它:
代碼框架與核心邏輯
async function activityTable(day) {
// 1. 讀取日志文件列表
let logFileList = await textFile("camera_logs.txt");
// 2. 初始化24小時(shí)計(jì)數(shù)器數(shù)組
const hourlyCounts = Array(24).fill(0);
// 3. 解析日志文件列表并并行處理每個(gè)日志文件
const logFiles = logFileList.trim().split('\n');
await Promise.all(logFiles.map(async logFileName => {
// 4. 讀取單個(gè)日志文件內(nèi)容
const logContent = await textFile(logFileName);
// 5. 解析每行時(shí)間戳并過(guò)濾指定星期幾的記錄
logContent.trim().split('\n').forEach(timestamp => {
const date = new Date(Number(timestamp));
if (date.getDay() === day) {
const hour = date.getHours();
hourlyCounts[hour]++; // 增加對(duì)應(yīng)小時(shí)的計(jì)數(shù)
}
});
}));
// 6. 返回統(tǒng)計(jì)結(jié)果
return hourlyCounts;
}
關(guān)鍵步驟詳解
-
讀取日志文件列表:
let logFileList = await textFile("camera_logs.txt");- 調(diào)用
textFile異步獲取日志文件列表內(nèi)容 - 返回類似:
"log1.txt\nlog2.txt\nlog3.txt"
- 調(diào)用
-
初始化計(jì)數(shù)器數(shù)組:
const hourlyCounts = Array(24).fill(0);- 創(chuàng)建長(zhǎng)度為24的數(shù)組,初始值全為0
- 索引0-23對(duì)應(yīng)00:00-23:00小時(shí)
-
解析并并行處理日志文件:
const logFiles = logFileList.trim().split('\n'); await Promise.all(logFiles.map(...));- 將文件列表按行分割為數(shù)組
- 使用
Promise.all并行處理所有日志文件
-
處理單個(gè)日志文件:
const logContent = await textFile(logFileName); logContent.trim().split('\n').forEach(timestamp => { ... });- 讀取日志文件內(nèi)容(多行時(shí)間戳)
- 逐行解析每個(gè)時(shí)間戳
-
過(guò)濾并統(tǒng)計(jì)特定星期的活動(dòng):
const date = new Date(Number(timestamp)); if (date.getDay() === day) { const hour = date.getHours(); hourlyCounts[hour]++; }- 將時(shí)間戳轉(zhuǎn)換為 Date 對(duì)象
- 檢查是否為目標(biāo)星期(
getDay() === day) - 提取小時(shí)信息并更新對(duì)應(yīng)計(jì)數(shù)器
執(zhí)行流程示例
假設(shè) camera_logs.txt 包含:
monday.log
tuesday.log
執(zhí)行流程:
- 讀取
camera_logs.txt得到兩個(gè)日志文件名 - 并行讀取
monday.log和tuesday.log - 解析每個(gè)日志文件中的時(shí)間戳:
1695709940692 → 2023-09-26 13:32:20 (星期二, 13點(diǎn)) 1695701068331 → 2023-09-26 11:24:28 (星期二, 11點(diǎn)) 1695701189163 → 2023-09-25 11:26:29 (星期一, 11點(diǎn)) - 若查詢
day=1(星期一):- 僅統(tǒng)計(jì)
1695701189163這條記錄 -
hourlyCounts[11] = 1,其他小時(shí)為0
- 僅統(tǒng)計(jì)
優(yōu)化與注意事項(xiàng)
-
錯(cuò)誤處理:
- 應(yīng)添加對(duì)
textFile可能失敗的處理 - 示例:
try { const logContent = await textFile(logFileName); } catch (err) { console.error(`讀取日志文件 ${logFileName} 失敗:`, err); }
- 應(yīng)添加對(duì)
-
內(nèi)存優(yōu)化:
- 對(duì)于極大的日志文件,可考慮流式處理而非一次性加載
-
性能考量:
- 并行處理所有日志文件可能導(dǎo)致資源耗盡
- 可限制并發(fā)數(shù):
await Promise.all(chunkArray(logFiles, 5).map(async chunk => { await Promise.all(chunk.map(handleFile)); }));
烏鴉的數(shù)據(jù)分析智慧
在卡拉的安全系統(tǒng)中,這種時(shí)間分析可以幫助她:
- 識(shí)別最安全的活動(dòng)時(shí)段(如凌晨2-4點(diǎn))
- 預(yù)測(cè)人類巡邏的高峰期
- 優(yōu)化自己的作息以避開監(jiān)控密集時(shí)段
例如,通過(guò)分析 activityTable(1)(星期一)的結(jié)果,她可能發(fā)現(xiàn):
- 11:00-13:00 活動(dòng)頻繁 → 對(duì)應(yīng)人類午餐時(shí)間
- 03:00-05:00 幾乎無(wú)活動(dòng) → 理想的實(shí)驗(yàn)室工作時(shí)段
這種數(shù)據(jù)分析能力讓她在人類世界的夾縫中建立起自己的數(shù)字王國(guó),就像烏鴉利用城市垃圾桶的規(guī)律來(lái)規(guī)劃覓食路線一樣精準(zhǔn)。 ?????
練習(xí)題:原生 Promise 實(shí)現(xiàn)
題目要求:
不使用 async/await 語(yǔ)法,而是使用原生的 Promise 方法(如 .then()、.catch()、Promise.all())重寫之前的 activityTable 函數(shù)。
參考答案
function activityTable(day) {
return textFile("camera_logs.txt")
.then(logFileList => {
const logFiles = logFileList.trim().split('\n');
const hourlyCounts = Array(24).fill(0);
return Promise.all(logFiles.map(logFileName => {
return textFile(logFileName)
.then(logContent => {
logContent.trim().split('\n').forEach(timestamp => {
const date = new Date(Number(timestamp));
if (date.getDay() === day) {
hourlyCounts[date.getHours()]++;
}
});
})
.catch(err => {
console.error(`Error reading log file ${logFileName}:`, err);
});
}))
.then(() => hourlyCounts);
})
.catch(err => {
console.error("Error reading camera_logs.txt:", err);
throw err; // 可選:向上傳播錯(cuò)誤
});
}
關(guān)鍵點(diǎn)解析
-
Promise 鏈?zhǔn)秸{(diào)用:
- 不再使用
await,而是通過(guò).then()方法連接異步操作 - 每個(gè)
.then()接收前一個(gè) Promise 的結(jié)果
- 不再使用
-
并行處理:
- 使用
Promise.all()并行讀取所有日志文件 - 每個(gè)日志文件的處理結(jié)果(
hourlyCounts的更新)會(huì)被合并
- 使用
-
錯(cuò)誤處理:
- 使用
.catch()捕獲讀取文件時(shí)可能的錯(cuò)誤 - 可選:通過(guò)
throw err將錯(cuò)誤繼續(xù)向上傳播
- 使用
-
狀態(tài)管理:
-
hourlyCounts數(shù)組在閉包中被共享和修改 - 所有日志文件處理完成后,最終返回該數(shù)組
-
與 async/await 的對(duì)比
| 特性 | 原生 Promise 實(shí)現(xiàn) | async/await 實(shí)現(xiàn) |
|---|---|---|
| 代碼結(jié)構(gòu) | 鏈?zhǔn)秸{(diào)用(扁平化但嵌套仍存在) | 同步風(fēng)格(更線性) |
| 錯(cuò)誤處理 | 需要多個(gè) .catch() 或最終統(tǒng)一處理 |
單一的 try/catch 塊 |
| 調(diào)試難度 | 調(diào)用??赡芨鼜?fù)雜 | 更接近同步代碼的調(diào)試體驗(yàn) |
| 狀態(tài)共享 | 通過(guò)閉包隱式共享 | 顯式變量聲明 |
| 異步間隙可見性 | 更明顯(通過(guò) .then() 分隔) |
隱藏在 await 之后 |
烏鴉的編程哲學(xué)
卡拉可能會(huì)選擇原生 Promise 實(shí)現(xiàn),因?yàn)樗N近底層機(jī)制,讓她能更精確地控制異步流程。在她的 LED 控制系統(tǒng)中,這種細(xì)粒度控制可能意味著:
-
并行與順序的精確平衡:
// 先獲取配置文件,再并行控制屏幕 getConfig() .then(config => Promise.all( screens.map(screen => sendCommand(screen, config)) )) .then(results => updateStatus(results)); -
優(yōu)雅降級(jí)策略:
fetchData() .catch(() => useFallbackData()) .then(data => process(data)); -
資源限流:
// 控制并發(fā)數(shù),避免網(wǎng)絡(luò)過(guò)載 function processWithLimit(items, limit) { let index = 0; const next = () => index < items.length ? process(items[index++]).then(next) : Promise.resolve(); return Promise.all(Array(limit).fill().map(next)); }
就像烏鴉用不同的喙部動(dòng)作處理不同類型的堅(jiān)果一樣,掌握多種異步編程范式能讓開發(fā)者在不同場(chǎng)景下選擇最合適的工具。 ?????
實(shí)現(xiàn)Promise_all函數(shù)
如我們所見,給定一個(gè)Promise數(shù)組,Promise.all會(huì)返回一個(gè)新的Promise,它等待數(shù)組中的所有Promise完成。當(dāng)所有Promise都成功時(shí),新Promise以包含所有結(jié)果的數(shù)組成功解析;如果其中任何一個(gè)Promise失敗,新Promise立即失敗,并傳遞第一個(gè)失敗Promise的原因。
現(xiàn)在需要你自己實(shí)現(xiàn)一個(gè)類似的函數(shù),命名為Promise_all。
注意:Promise一旦成功或失敗,就不能再次改變狀態(tài),后續(xù)對(duì)其resolve/reject函數(shù)的調(diào)用會(huì)被忽略。這一特性可以簡(jiǎn)化我們對(duì)Promise失敗的處理邏輯。
實(shí)現(xiàn)思路
核心邏輯
-
初始化狀態(tài):
- 記錄已成功解析的Promise數(shù)量
- 存儲(chǔ)所有Promise的解析結(jié)果(按原始順序)
- 捕獲第一個(gè)失敗的Promise的原因
-
遍歷處理每個(gè)Promise:
- 為每個(gè)Promise綁定成功和失敗回調(diào)
- 成功時(shí):將結(jié)果存入對(duì)應(yīng)位置,若所有Promise已成功,解析最終結(jié)果
- 失敗時(shí):若尚未有失敗記錄,立即拒絕最終Promise
-
處理非Promise值:
- 若數(shù)組元素不是Promise,先將其轉(zhuǎn)換為已解析的Promise
參考實(shí)現(xiàn)
function Promise_all(promises) {
return new Promise((resolve, reject) => {
const results = new Array(promises.length); // 按順序存儲(chǔ)結(jié)果
let completed = 0; // 已成功解析的Promise數(shù)量
// 處理數(shù)組中的每個(gè)元素(可能是非Promise)
promises.forEach((promise, index) => {
// 將非Promise值轉(zhuǎn)換為已解析的Promise
Promise.resolve(promise)
.then(value => {
results[index] = value; // 按索引存儲(chǔ)結(jié)果
completed++; // 成功計(jì)數(shù)+1
// 所有Promise都成功時(shí),解析最終結(jié)果
if (completed === promises.length) {
resolve(results);
}
})
.catch(error => {
// 第一個(gè)失敗的Promise決定最終結(jié)果
if (!rejectCalled) { // 確保只處理一次失敗
reject(error);
rejectCalled = true; // 標(biāo)記已失敗
}
});
});
// 處理空數(shù)組的情況(所有Promise已完成)
if (promises.length === 0) {
resolve([]);
}
});
}
關(guān)鍵點(diǎn)解析
-
非Promise元素處理:
Promise.resolve(promise)- 確保數(shù)組中的每個(gè)元素都是Promise,統(tǒng)一用
.then()處理
- 確保數(shù)組中的每個(gè)元素都是Promise,統(tǒng)一用
-
順序性保證:
- 使用索引
index將結(jié)果存入對(duì)應(yīng)的位置 - 最終結(jié)果數(shù)組的順序與輸入數(shù)組一致
- 使用索引
-
失敗處理優(yōu)化:
- 通過(guò)
rejectCalled標(biāo)記確保只處理第一個(gè)失敗的Promise - 后續(xù)的失敗會(huì)被忽略,符合Promise的狀態(tài)不可變特性
- 通過(guò)
-
空數(shù)組處理:
- 若輸入數(shù)組為空,直接解析空數(shù)組
resolve([]),與原生Promise.all([])行為一致
- 若輸入數(shù)組為空,直接解析空數(shù)組
測(cè)試用例
測(cè)試1:所有Promise成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
Promise_all([p1, p2]).then(results => {
console.log(results); // 輸出: [1, 2]
});
測(cè)試2:包含非Promise值
Promise_all([1, Promise.resolve(2), 3]).then(results => {
console.log(results); // 輸出: [1, 2, 3]
});
測(cè)試3:Promise失敗
const p1 = Promise.resolve(1);
const p2 = Promise.reject(new Error("Fail"));
Promise_all([p1, p2]).catch(error => {
console.error(error.message); // 輸出: "Fail"
});
測(cè)試4:空數(shù)組
Promise_all([]).then(results => {
console.log(results); // 輸出: []
});
烏鴉的算法智慧
在卡拉的機(jī)器人軍團(tuán)控制程序中,類似Promise_all的機(jī)制用于協(xié)調(diào)多個(gè)機(jī)器人的動(dòng)作:
function coordinateRobots(robots) {
return Promise_all(
robots.map(robot =>
robot.moveTo(targetPosition)
.then(() => `Robot ${robot.id} arrived`)
.catch(err => `Robot ${robot.id} failed: ${err}`)
)
);
}
這種實(shí)現(xiàn)確保:
- 所有機(jī)器人動(dòng)作并行執(zhí)行
- 任何一個(gè)機(jī)器人失敗都會(huì)立即通知主控系統(tǒng)
- 最終結(jié)果按機(jī)器人原始順序返回狀態(tài)
就像烏鴉群協(xié)作時(shí)需要同步行動(dòng)一樣,Promise_all讓異步操作在保持獨(dú)立性的同時(shí),能以整體視角處理成功與失敗。這種“統(tǒng)一協(xié)調(diào),快速響應(yīng)”的機(jī)制,正是異步編程中處理復(fù)雜場(chǎng)景的核心智慧。 ?????