最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript循環(huán)中異步處理之for、forEach、for...of、for...in的區(qū)別詳解

 更新時(shí)間:2026年03月03日 09:58:53   作者:Fantastic_sj  
在JavaScript中,遍歷數(shù)組和對(duì)象是前端開(kāi)發(fā)中常見(jiàn)的任務(wù)之一,而為了完成這項(xiàng)任務(wù),開(kāi)發(fā)者們通常會(huì)使用不同類(lèi)型的循環(huán),這篇文章主要介紹了JavaScript循環(huán)異步處理之for、forEach、for...of、for...in區(qū)別的相關(guān)資料,需要的朋友可以參考下

一、同步循環(huán)與異步循環(huán)的核心區(qū)別

同步循環(huán)(阻塞執(zhí)行)

// 同步執(zhí)行:每個(gè)循環(huán)完全執(zhí)行完才進(jìn)入下一個(gè)
for (let i = 0; i < 3; i++) {
  console.log(`同步 ${i} 開(kāi)始`);
  const result = syncTask(i); // 同步任務(wù)
  console.log(`同步 ${i} 結(jié)束: ${result}`);
}
// 輸出順序:同步0開(kāi)始 → 同步0結(jié)束 → 同步1開(kāi)始 → 同步1結(jié)束 → 同步2開(kāi)始 → 同步2結(jié)束

異步循環(huán)(非阻塞執(zhí)行)

// 異步執(zhí)行:不等異步任務(wù)完成就繼續(xù)下一個(gè)循環(huán)
for (let i = 0; i < 3; i++) {
  console.log(`異步 ${i} 開(kāi)始`);
  asyncTask(i).then(result => {
    console.log(`異步 ${i} 結(jié)束: ${result}`);
  });
}
// 輸出順序:異步0開(kāi)始 → 異步1開(kāi)始 → 異步2開(kāi)始 → (異步任務(wù)結(jié)果隨機(jī)出現(xiàn))

二、不同循環(huán)方式的異步行為對(duì)比

1. 普通 for 循環(huán)

// 基本語(yǔ)法
for (let i = 0; i < 3; i++) {
  // 循環(huán)體
}

異步特性分析:

console.log('開(kāi)始');

for (let i = 0; i < 3; i++) {
  console.log(`循環(huán) ${i} 開(kāi)始`);
  
  // 情況1:包含異步操作
  setTimeout(() => {
    console.log(`setTimeout ${i}`);
  }, Math.random() * 100);
  
  // 情況2:包含Promise
  Promise.resolve()
    .then(() => console.log(`Promise ${i}`));
}

console.log('結(jié)束');

// 輸出順序:
// 開(kāi)始
// 循環(huán)0開(kāi)始
// 循環(huán)1開(kāi)始
// 循環(huán)2開(kāi)始
// 結(jié)束
// Promise 0
// Promise 1
// Promise 2
// setTimeout 0(時(shí)間隨機(jī))
// setTimeout 1
// setTimeout 2

2. forEach 循環(huán)

// 基本語(yǔ)法
[0, 1, 2].forEach((item, index) => {
  // 循環(huán)體
});

異步特性分析:

console.log('開(kāi)始');

[0, 1, 2].forEach(async (item, index) => {
  console.log(`forEach ${index} 開(kāi)始`);
  
  // 異步操作無(wú)法阻塞forEach
  await new Promise(resolve => 
    setTimeout(resolve, Math.random() * 100)
  );
  
  console.log(`forEach ${index} 結(jié)束`);
});

console.log('結(jié)束');

// 輸出順序:
// 開(kāi)始
// forEach 0開(kāi)始
// forEach 1開(kāi)始
// forEach 2開(kāi)始
// 結(jié)束
// (await結(jié)果隨機(jī)出現(xiàn))
// forEach 1結(jié)束
// forEach 0結(jié)束
// forEach 2結(jié)束

3. for...of 循環(huán)

// 基本語(yǔ)法
for (const item of [0, 1, 2]) {
  // 循環(huán)體
}

異步特性分析:

console.log('開(kāi)始');

for (const item of [0, 1, 2]) {
  console.log(`for...of ${item} 開(kāi)始`);
  
  // 可以使用await
  await new Promise(resolve => 
    setTimeout(resolve, 100)
  );
  
  console.log(`for...of ${item} 結(jié)束`);
}

console.log('結(jié)束');

// 輸出順序:
// 開(kāi)始
// for...of 0開(kāi)始
// (等待100ms)
// for...of 0結(jié)束
// for...of 1開(kāi)始
// (等待100ms)
// for...of 1結(jié)束
// for...of 2開(kāi)始
// (等待100ms)
// for...of 2結(jié)束
// 結(jié)束

4. for...in 循環(huán)

// 基本語(yǔ)法
for (const key in object) {
  // 循環(huán)體
}

異步特性分析:

const obj = { a: 1, b: 2, c: 3 };

console.log('開(kāi)始');

for (const key in obj) {
  console.log(`for...in ${key} 開(kāi)始`);
  
  // 遍歷對(duì)象屬性,同樣支持await
  await new Promise(resolve => 
    setTimeout(resolve, Math.random() * 100)
  );
  
  console.log(`for...in ${key} 結(jié)束`);
}

console.log('結(jié)束');

// 輸出順序:順序執(zhí)行,但遍歷順序可能因JS引擎而異

三、循環(huán)與 Promise 的配合方式

1. 串行執(zhí)行(一個(gè)接一個(gè))

// 方法1:使用 async/await + for...of
async function serialExecution() {
  const items = [1, 2, 3];
  
  for (const item of items) {
    console.log(`開(kāi)始處理 ${item}`);
    
    // 等待當(dāng)前Promise完成后再繼續(xù)下一個(gè)
    const result = await processItem(item);
    
    console.log(`完成處理 ${item}: ${result}`);
  }
  
  console.log('所有任務(wù)串行完成');
}

// 方法2:使用 reduce 鏈?zhǔn)秸{(diào)用
function serialExecutionWithReduce() {
  const items = [1, 2, 3];
  
  return items.reduce((promiseChain, item) => {
    return promiseChain.then(() => {
      console.log(`開(kāi)始處理 ${item}`);
      return processItem(item);
    }).then(result => {
      console.log(`完成處理 ${item}: ${result}`);
    });
  }, Promise.resolve())
  .then(() => console.log('所有任務(wù)串行完成'));
}

// 串行執(zhí)行示例
const processItem = (item) => 
  new Promise(resolve => 
    setTimeout(() => resolve(`結(jié)果${item}`), 100)
  );

// 輸出順序:按順序一個(gè)個(gè)執(zhí)行,總耗時(shí) = 每個(gè)任務(wù)耗時(shí)之和

2. 并行執(zhí)行(同時(shí)開(kāi)始)

// 方法1:使用 Promise.all
async function parallelExecution() {
  const items = [1, 2, 3];
  
  console.log('所有任務(wù)同時(shí)開(kāi)始');
  
  // 同時(shí)啟動(dòng)所有Promise
  const promises = items.map(item => {
    console.log(`啟動(dòng)任務(wù) ${item}`);
    return processItem(item);
  });
  
  // 等待所有Promise完成
  const results = await Promise.all(promises);
  
  console.log('所有任務(wù)并行完成:', results);
}

// 方法2:使用 forEach(但無(wú)法獲取結(jié)果)
function parallelExecutionWithForEach() {
  const items = [1, 2, 3];
  
  items.forEach(async (item) => {
    const result = await processItem(item);
    console.log(`任務(wù) ${item} 完成: ${result}`);
  });
  
  console.log('所有任務(wù)已啟動(dòng)(但無(wú)法等待全部完成)');
}

// 并行執(zhí)行示例
const processItem = (item) => 
  new Promise(resolve => 
    setTimeout(() => resolve(`結(jié)果${item}`), Math.random() * 200)
  );

// 輸出順序:同時(shí)開(kāi)始,完成順序隨機(jī),總耗時(shí) = 最慢的任務(wù)耗時(shí)

3. 限制并發(fā)數(shù)(同時(shí)執(zhí)行N個(gè))

// 方法1:使用 async-pool 庫(kù)思想
async function concurrentExecution(concurrency = 2) {
  const items = [1, 2, 3, 4, 5];
  const results = [];
  const executing = [];
  
  for (const item of items) {
    // 創(chuàng)建Promise
    const p = processItem(item).then(result => {
      results.push({ item, result });
      console.log(`任務(wù) ${item} 完成`);
    });
    
    // 保存Promise引用
    const e = p.then(() => 
      executing.splice(executing.indexOf(e), 1)
    );
    executing.push(e);
    
    // 如果達(dá)到并發(fā)限制,等待其中一個(gè)完成
    if (executing.length >= concurrency) {
      await Promise.race(executing);
    }
  }
  
  // 等待所有剩余任務(wù)完成
  await Promise.all(executing);
  console.log('所有任務(wù)完成:', results);
}

// 方法2:使用更簡(jiǎn)潔的實(shí)現(xiàn)
async function concurrentExecutionSimple(items, concurrency = 2) {
  const batches = [];
  
  for (let i = 0; i < items.length; i += concurrency) {
    batches.push(items.slice(i, i + concurrency));
  }
  
  for (const batch of batches) {
    // 并行執(zhí)行當(dāng)前批次
    const promises = batch.map(item => processItem(item));
    const results = await Promise.all(promises);
    
    console.log(`批次完成:`, results);
  }
}

// 示例:同時(shí)最多執(zhí)行2個(gè)任務(wù)
const processItem = (item) => 
  new Promise(resolve => 
    setTimeout(() => {
      console.log(`處理中: ${item}`);
      resolve(`結(jié)果${item}`);
    }, 1000)
  );

// 輸出:同時(shí)執(zhí)行2個(gè),完成一批再執(zhí)行下一批

4. 循環(huán)中處理錯(cuò)誤的策略

// 方法1:串行執(zhí)行,一個(gè)失敗不影響后續(xù)
async function serialWithErrorHandling() {
  const items = [1, 2, 3, 'error', 5];
  const results = [];
  
  for (const item of items) {
    try {
      const result = await processWithError(item);
      results.push({ item, result });
    } catch (error) {
      console.error(`任務(wù) ${item} 失敗:`, error.message);
      results.push({ item, error: error.message });
    }
  }
  
  console.log('串行完成,有錯(cuò)誤繼續(xù)執(zhí)行:', results);
}

// 方法2:并行執(zhí)行,使用 allSettled
async function parallelWithErrorHandling() {
  const items = [1, 2, 3, 'error', 5];
  
  const promises = items.map(item => 
    processWithError(item)
      .then(result => ({ status: 'fulfilled', value: result }))
      .catch(error => ({ status: 'rejected', reason: error.message }))
  );
  
  const results = await Promise.allSettled(promises);
  console.log('并行完成,處理所有結(jié)果:', results);
}

// 模擬可能失敗的任務(wù)
const processWithError = (item) => 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      if (item === 'error') {
        reject(new Error('故意失敗'));
      } else {
        resolve(`結(jié)果${item}`);
      }
    }, 100);
  });

四、不同循環(huán)方式在異步場(chǎng)景下的詳細(xì)對(duì)比

對(duì)比表格

循環(huán)方式是否支持 await執(zhí)行順序適合場(chǎng)景性能特點(diǎn)
普通 for? 支持順序執(zhí)行需要索引控制最快,但需要手動(dòng)處理異步
for...of? 支持順序執(zhí)行需要順序執(zhí)行的異步支持異步,語(yǔ)法簡(jiǎn)潔
forEach? 不支持(async無(wú)效)并發(fā)啟動(dòng)純同步或不需要等待無(wú)法等待異步完成
for...in? 支持順序執(zhí)行(屬性順序不確定)遍歷對(duì)象屬性遍歷對(duì)象時(shí)使用
map? 不支持(但可返回Promise數(shù)組)并發(fā)啟動(dòng)需要轉(zhuǎn)換數(shù)組并并行處理返回新數(shù)組,適合Promise.all

實(shí)際場(chǎng)景示例對(duì)比

// 場(chǎng)景:處理API請(qǐng)求數(shù)組
const apiUrls = [
  'https://api.example.com/1',
  'https://api.example.com/2',
  'https://api.example.com/3'
];

// 方案1:forEach ?(錯(cuò)誤示范)
apiUrls.forEach(async (url) => {
  const data = await fetch(url).then(r => r.json()); // 無(wú)法正確等待
  console.log(data); // 可能不會(huì)按預(yù)期執(zhí)行
});
console.log('forEach結(jié)束'); // 會(huì)立即執(zhí)行

// 方案2:for...of ?(正確示范)
async function processWithForOf() {
  for (const url of apiUrls) {
    const data = await fetch(url).then(r => r.json());
    console.log('for...of:', data); // 順序執(zhí)行,等待每個(gè)完成
  }
  console.log('for...of結(jié)束'); // 所有完成后執(zhí)行
}

// 方案3:Promise.all + map ?(并行正確示范)
async function processWithPromiseAll() {
  const promises = apiUrls.map(url => 
    fetch(url).then(r => r.json())
  );
  const results = await Promise.all(promises);
  console.log('Promise.all結(jié)果:', results); // 所有結(jié)果數(shù)組
  console.log('Promise.all結(jié)束'); // 所有完成后執(zhí)行
}

// 方案4:reduce實(shí)現(xiàn)串行 ?
async function processWithReduce() {
  await apiUrls.reduce(async (prevPromise, url) => {
    await prevPromise; // 等待上一個(gè)完成
    const data = await fetch(url).then(r => r.json());
    console.log('reduce:', data);
    return data; // 傳遞給下一個(gè)迭代
  }, Promise.resolve());
  console.log('reduce結(jié)束');
}

五、性能對(duì)比與最佳實(shí)踐

性能測(cè)試代碼

// 性能測(cè)試函數(shù)
async function performanceTest() {
  const items = Array.from({ length: 100 }, (_, i) => i);
  
  const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
  
  // 測(cè)試1:串行執(zhí)行
  console.time('serial-for-of');
  for (const item of items) {
    await delay(10);
  }
  console.timeEnd('serial-for-of'); // ~1000ms
  
  // 測(cè)試2:并行執(zhí)行
  console.time('parallel-promise-all');
  const promises = items.map(() => delay(10));
  await Promise.all(promises);
  console.timeEnd('parallel-promise-all'); // ~10ms
  
  // 測(cè)試3:forEach(不等待)
  console.time('forEach');
  items.forEach(async () => {
    await delay(10);
  });
  console.timeEnd('forEach'); // <1ms(但不保證完成)
}

// 測(cè)試結(jié)果分析
// 串行:總時(shí)間 = 單個(gè)任務(wù)時(shí)間 × 任務(wù)數(shù)量
// 并行:總時(shí)間 ≈ 最慢的任務(wù)時(shí)間
// forEach:只測(cè)量啟動(dòng)時(shí)間,不測(cè)量完成時(shí)間

最佳實(shí)踐總結(jié)

// 規(guī)則1:需要順序執(zhí)行時(shí) → 使用 for...of 或 for + await
async function processSequentially(items) {
  for (const item of items) {
    const result = await processItem(item); // 等待前一個(gè)完成
    // 處理結(jié)果
  }
}

// 規(guī)則2:需要并行執(zhí)行時(shí) → 使用 Promise.all + map
async function processInParallel(items) {
  const promises = items.map(item => processItem(item));
  const results = await Promise.all(promises); // 同時(shí)等待所有
  // 處理所有結(jié)果
}

// 規(guī)則3:需要限制并發(fā)時(shí) → 使用自定義并發(fā)控制
async function processWithConcurrency(items, limit = 5) {
  const batches = [];
  for (let i = 0; i < items.length; i += limit) {
    batches.push(items.slice(i, i + limit));
  }
  
  for (const batch of batches) {
    await Promise.all(batch.map(processItem));
  }
}

// 規(guī)則4:需要處理錯(cuò)誤且繼續(xù) → 使用 try-catch 或 allSettled
async function processWithErrorHandling(items) {
  // 方式1:逐個(gè)處理錯(cuò)誤
  for (const item of items) {
    try {
      await processItem(item);
    } catch (error) {
      console.error('失敗但繼續(xù):', error);
    }
  }
  
  // 方式2:并行處理所有結(jié)果
  const results = await Promise.allSettled(
    items.map(item => processItem(item))
  );
}

// 規(guī)則5:避免在forEach中使用async
// ? 錯(cuò)誤
items.forEach(async (item) => {
  await processItem(item); // 不會(huì)等待
});

// ? 正確
for (const item of items) {
  await processItem(item); // 會(huì)等待
}

六、高級(jí)應(yīng)用場(chǎng)景

1. 帶超時(shí)控制的循環(huán)處理

async function processWithTimeout(items, timeout = 5000) {
  const results = [];
  
  for (const item of items) {
    try {
      // 創(chuàng)建超時(shí)Promise
      const timeoutPromise = new Promise((_, reject) => 
        setTimeout(() => reject(new Error('超時(shí)')), timeout)
      );
      
      // 競(jìng)速:業(yè)務(wù)Promise vs 超時(shí)Promise
      const result = await Promise.race([
        processItem(item),
        timeoutPromise
      ]);
      
      results.push({ item, result, status: 'success' });
    } catch (error) {
      results.push({ item, error: error.message, status: 'timeout' });
    }
  }
  
  return results;
}

2. 帶進(jìn)度報(bào)告的循環(huán)處理

async function processWithProgress(items, onProgress) {
  const total = items.length;
  let completed = 0;
  
  // 并行處理但跟蹤進(jìn)度
  const promises = items.map((item, index) => 
    processItem(item).then(result => {
      completed++;
      onProgress({
        completed,
        total,
        percent: Math.round((completed / total) * 100),
        current: item,
        index
      });
      return result;
    })
  );
  
  const results = await Promise.all(promises);
  onProgress({ completed: total, total, percent: 100 });
  return results;
}

// 使用
processWithProgress([1, 2, 3, 4, 5], (progress) => {
  console.log(`進(jìn)度: ${progress.percent}%`);
});

3. 遞歸異步循環(huán)

async function recursiveAsyncProcess(items, index = 0, results = []) {
  if (index >= items.length) {
    return results;
  }
  
  const item = items[index];
  const result = await processItem(item);
  results.push(result);
  
  // 遞歸調(diào)用下一個(gè)
  return recursiveAsyncProcess(items, index + 1, results);
}

// 尾遞歸優(yōu)化版本
async function tailRecursiveAsyncProcess(items, index = 0, accumulator = []) {
  if (index >= items.length) {
    return accumulator;
  }
  
  const result = await processItem(items[index]);
  accumulator.push(result);
  
  // 直接返回遞歸調(diào)用
  return tailRecursiveAsyncProcess(items, index + 1, accumulator);
}

七、總結(jié)與決策樹(shù)

選擇循環(huán)方式的決策流程:

是否需要在循環(huán)中使用 await?
├── 是 → 選擇 for...of 或 普通for循環(huán)
└── 否 → 
    ├── 是否需要并行執(zhí)行所有任務(wù)?
    │   ├── 是 → 使用 map + Promise.all
    │   └── 否 → 
    │       ├── 是否需要遍歷對(duì)象屬性?
    │       │   ├── 是 → 使用 for...in
    │       │   └── 否 → 使用 forEach
    └── 是否需要限制并發(fā)數(shù)?
        ├── 是 → 使用并發(fā)控制函數(shù)
        └── 否 → 已解決

是否需要在任務(wù)失敗時(shí)繼續(xù)執(zhí)行?
├── 是 → 使用 try-catch(串行)或 Promise.allSettled(并行)
└── 否 → 直接使用

是否需要獲取所有結(jié)果?
├── 是 → 確保使用返回結(jié)果的模式
└── 否 → 可以選擇只執(zhí)行的模式

核心記憶點(diǎn)

  • forEach 中的 async 是無(wú)效的,它不會(huì)等待異步操作完成

  • for...of 支持 await,可以實(shí)現(xiàn)真正的異步順序執(zhí)行

  • Promise.all + map 是最常用的并行執(zhí)行模式

  • 并發(fā)控制 需要手動(dòng)實(shí)現(xiàn),避免資源耗盡

  • 錯(cuò)誤處理 要根據(jù)場(chǎng)景選擇 try-catch 或 allSettled

正確理解和應(yīng)用這些循環(huán)與Promise的配合方式,可以有效管理異步操作,避免常見(jiàn)的并發(fā)問(wèn)題和性能瓶頸。

總結(jié)

到此這篇關(guān)于JavaScript循環(huán)中異步處理之for、forEach、for...of、for...in區(qū)別詳解的文章就介紹到這了,更多相關(guān)JS循環(huán)異步處理for、forEach、for...of、for...in內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

南开区| 喀喇沁旗| 邹平县| 新泰市| 申扎县| 新源县| 安塞县| 萍乡市| 葵青区| 囊谦县| 玉田县| 壤塘县| 淮北市| 彰武县| 崇州市| 新宁县| 固阳县| 博客| 伊宁县| 天全县| 林州市| 尉氏县| 厦门市| 安庆市| 丹巴县| 长宁区| 荔波县| 庆元县| 青河县| 阿城市| 景洪市| 平阳县| 山丹县| 长春市| 烟台市| 揭东县| 白银市| 旌德县| 临泉县| 大足县| 大同市|