偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

別再以為 await 到處都一樣:這只是“半個(gè)真相”

開發(fā) 前端
異步 JS 很“怪”。教程常這么教:函數(shù)前加?async,Promise 前放?await,完事兒。 但在循環(huán) + 異步的組合里:并不是所有循環(huán)都懂得等待。

大多數(shù)前端以為:有了 await,代碼哪里都能暫停。 抱歉——這話只對了一半。

await 的行為,會因?yàn)?/span>使用場景而劇烈變化。 尤其是:你選的循環(huán)方式,會徹底改變await 的效果。

這是我真希望 6 個(gè)月前就有人告訴我的事,省下我一堆 async/await 的坑。

核心誤解:會“等”的不是所有地方

異步 JS 很“怪”。教程常這么教:函數(shù)前加 async,Promise 前放 await,完事兒。 但在循環(huán) + 異步的組合里:并不是所有循環(huán)都懂得等待

有的直接無視你的 await; 有的全部并行(即便你不想); 還有的會默默給你整崩。

這里是 await 的“墳場”:forEach

最常見、傷害最大的錯(cuò)誤:**在 forEach 里用 await**。

async function fetchUserProfiles() {
  const userIds = [1, 2, 3, 4, 5]
  
  userIds.forEach(async (id) => {
    const profile = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
    const data = await profile.json()
    console.log(`User ${id}:`, data.name)
  })
  
  console.log('All done fetching users')
}

fetchUserProfiles()

運(yùn)行看看:

  • “All done fetching users” 會立刻打印,在任何 fetch 完成之前。
  • 控制臺順序亂序:2、5、1、3、4……隨緣。

原因forEach不關(guān)心 Promise。你的回調(diào)是 async,會返回 Promise,但 forEach直接忽略返回值,調(diào)用完就走。 你也沒法 await forEach,因?yàn)樗环祷赜杏玫臇|西。

看起來像對的語法,邏輯也似乎“合理”,但——**forEach 天生不為 Promise 設(shè)計(jì)**。

map 與 Promise 的“隱形坑”

這段代碼在 Review 里很常見,也很迷惑

async function getAllUserData() {
  const userIds = [1, 2, 3]
  
  const userData = await userIds.map(async (id) => {
    const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`)
    return response.json()
  })
  
  console.log(userData)
}

你以為能拿到用戶對象數(shù)組?不,你會得到Promise 數(shù)組。await userIds.map(...)并不會等待里面的異步完成;map 只會收集回調(diào)返回的 Promise

正確寫法:先收集 Promise,再 Promise.all 一把梭。

async function getAllUserData() {
  const userIds = [1, 2, 3]
  
  const userData = await Promise.all(
    userIds.map(id => 
      fetch(`https://pokeapi.co/api/v2/pokemon/${id}`).then(res => res.json())
    )
  )
  
  console.log(userData)
}

不小心的“串行殺性能”

確實(shí)有場景需要嚴(yán)格順序執(zhí)行,這時(shí)用 for…of 很香:

async function processSequentially() {
  const ids = [1, 2, 3]
  
  for (const id of ids) {
    const result = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
    const data = await result.json()
    console.log(`Processed ${id}:`, data)
  }
  
  console.log('Done')
}

這段代碼直觀、可讀、靠譜。 但要小心:純串行意味著每個(gè)都要等上一個(gè)。 如果每次請求 1s,五次就5s。只有在確實(shí)存在依賴/限速/必須有序時(shí)才用串行;否則你就白白慢了 5 倍。

為什么 for…of 會“真的等”

for…of 是語言級的循環(huán),不是數(shù)組方法。 在 for…of 里用 await,整個(gè)循環(huán)會在該處暫停,直到 Promise 解決:

const userIds = [1, 2, 3];

for (const id of userIds) {
  const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`); // 這里暫停
  const data = await response.json();
  console.log(data); // 等待后才繼續(xù)下一輪
}

這與無法暫停的 forEach 本質(zhì)不同。使用場景強(qiáng)順序、節(jié)流/限流、結(jié)果必須按序。

要速度,就上 Promise.all 并行

當(dāng)任務(wù)互不依賴,且你追求吞吐

async function processInParallel() {
  const ids = [1, 2, 3, 4, 5]
  
  const promises = ids.map(id => 
    fetch(`https://pokeapi.co/api/v2/pokemon/${id}`).then(res => res.json())
  )
  
  const results = await Promise.all(promises)
  console.log('All results:', results)
}

如果每個(gè)耗時(shí) 1s,并行就是**~1s 完成(不考慮帶寬/服務(wù)端限制)。順序保持與原數(shù)組一致,即便完成順序不同。注意:Promise.all失敗即全部失敗——任何一個(gè) reject,整體直接 reject**,中間結(jié)果拿不到。

更韌性的并行:Promise.allSettled

想要“不管成功失敗都收集”

async function fetchAllSources() {
  const sources = [
    'https://api1.com/data',
    'https://api2.com/data',
    'https://api3.com/data'
  ]
  
  const promises = sources.map(url => 
    fetch(url).then(res => res.json())
  )
  
  const results = await Promise.allSettled(promises)
  
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`Source ${index} succeeded:`, result.value)
    } else {
      console.error(`Source ${index} failed:`, result.reason)
    }
  })
}

allSettled 會等所有 Promise 都 settle(resolve/reject 均可),然后給你完整戰(zhàn)況。

能不用就別用的:reduce + await

在 reduce 里玩 await,讀起來繞、調(diào)試更繞:

const userIds = [1, 2, 3];

async function fetchUsers() {
  const users = await userIds.reduce(async (accPromise, id) => {
    const acc = await accPromise;
    const user = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
    return [...acc, user];
  }, Promise.resolve([]));
  
  console.log(users);
}

fetchUsers();

累加器從第二輪開始就是 Promise,你還得層層 await。 這通常比 for…of 或 Promise.all更難讀也更慢。 除非你確實(shí)在構(gòu)建“步步相依”的序列,否則別選它。

進(jìn)階:可控并發(fā)(批處理)

全串行太慢、全并發(fā)會被限流?中間態(tài):分批并發(fā)。

async function processInBatches(items, batchSize = 5) {
  const results = []
  
  for (let i = 0; i < items.length; i += batchSize) {
    const batch = items.slice(i, i + batchSize)
    const batchResults = await Promise.all(
      batch.map(item => 
        fetch(`https://pokeapi.co/api/v2/pokemon/${item}`).then(res => res.json())
      )
    )
    results.push(...batchResults)
  }
  
  return results
}

每批最多 5 個(gè),請求并發(fā)完成后再進(jìn)下一批:性能優(yōu)于純串行,風(fēng)險(xiǎn)低于無限并發(fā),非常適合第三方 API 的并發(fā)上限。

最后的要點(diǎn)

下次你想在循環(huán)里敲上一個(gè) await,先停半秒,想清楚你要的到底是:

  • 強(qiáng)順序for…of),
  • 極速并行Promise.all),
  • 韌性收集Promise.allSettled),
  • 還是限流批處理(分批 Promise.all)。

玩一玩這些模式,歡迎在評論里補(bǔ)充你的最佳實(shí)踐。

責(zé)任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2024-04-10 07:56:38

前端數(shù)組uniq

2012-06-12 09:43:11

jQuery

2022-04-14 19:39:39

Java線程安全

2023-04-30 23:16:53

Go數(shù)組切片

2011-02-28 10:38:13

Windows 8

2012-12-20 10:17:32

IT運(yùn)維

2012-03-07 17:24:10

戴爾咨詢

2023-02-08 15:38:16

首席信息官IT

2009-06-12 15:26:02

2015-08-25 09:52:36

云計(jì)算云計(jì)算產(chǎn)業(yè)云計(jì)算政策

2013-01-11 18:10:56

軟件

2022-08-19 10:27:39

系統(tǒng)模型

2023-04-05 14:19:07

FlinkRedisNoSQL

2014-06-05 11:25:10

2024-11-29 14:41:55

2017-09-16 19:31:29

Wi-Fi路由器輻射

2018-10-25 14:40:23

分布式數(shù)據(jù)數(shù)據(jù)不一致

2017-05-25 15:02:46

聯(lián)宇益通SD-WAN

2015-10-19 12:33:01

華三/新IT

2022-05-05 10:02:06

Java設(shè)計(jì)模式開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號