极简三分钟ES6 - ES9中for await of

33 阅读2分钟

快递站取快递

想象你同时网购了3件商品(对应3个异步任务)

  • 普通for...of:必须按顺序取快递(等第一个到了才能取第二个)
  • for await...of:快递站到货通知你(哪个先到先取哪个)
// 模拟三个快递请求  
const fetchPackages = [  
  fetch('//快递1').then(r => r.json()),   
  fetch('//快递2').then(r => r.json()),   
  fetch('//快递3').then(r => r.json())   
];  
 
// 使用for await...of取快递  
async function getDeliveries() {  
  for await (const pkg of fetchPackages) {  
    console.log(` 收到包裹:${pkg.name}`);   
  }  
}  

特性详解

能遍历异步迭代器(Async Iterable)

  • 普通数组/Set/Map → 直接遍历
  • 异步数据源(如网络流/数据库查询)→ 等待每个值完成再继续
// 创建异步迭代器(每秒生成一个随机数)  
const asyncRandom = {  
  [Symbol.asyncIterator]: async function*() {  
    while(true) {  
      await new Promise(resolve => setTimeout(resolve, 1000));  
      yield Math.random();   
    }  
  }  
};  
 
// 遍历异步迭代器  
(async () => {  
  for await (const num of asyncRandom) {  
    if(num > 0.9) break;  
    console.log(` 当前数值:${num}`);  
  }  
})();  

与同步迭代器的区别

特性for...offor await...of
迭代器类型同步迭代器异步迭代器
等待机制同步执行每个值都等待完成
适用数据源Array/Set/Map等流/WebSocket/数据库

错误处理:try/catch包裹

async function readStream() {  
  try {  
    for await (const chunk of networkStream) {  
      process(chunk);  
    }  
  } catch (err) {  
    console.error(" 数据流中断:", err);  
  }  
}  

一些常见的使用场景

分页API的连续请求

async function getAllPages() {  
  const pageRequests = [  
    fetch('//api?page=1'),  
    fetch('//api?page=2'),  
    fetch('//api?page=3')  
  ];  
    
  // 哪个接口先响应先处理哪个  
  for await (const response of pageRequests) {  
    const data = await response.json();   
    render(data);  
  }  
}  

读取大文件流

import { createReadStream } from 'fs';  
 
async function readBigFile() {  
  const stream = createReadStream('data.log');   
  for await (const chunk of stream) {  
    console.log(` 收到${chunk.length} 字节数据`);  
  }  
} 

WebSocket消息处理

const socket = new WebSocket('wss://实时数据');  
 
async function handleMessages() {  
  for await (const msg of socket) { // 需实现异步迭代器  
    if (msg.type  === 'ALERT') notifyUser(msg);  
  }  
}  

数据库批量查询

async function exportUserData() {  
  const query = db.query('SELECT  * FROM users');  
  for await (const user of query) {  
    writeToCSV(user); // 逐条写入CSV  
  }  
}  

并发任务状态监控

const tasks = [  
  backupDatabase(), // 返回Promise  
  uploadLogs(),  
  sendEmails()  
];  
 
async function monitorTasks() {  
  for await (const result of tasks) {  
    updateProgressBar(result.taskId);   
  }  
}  

一些特殊场景

同步与异步混合遍历

const mixedSources = [  
  Promise.resolve(' 异步苹果'),  
  '同步香蕉',  
  Promise.resolve(' 异步橙子')  
];  
 
for await (const item of mixedSources) {  
  console.log(item);  // 正常输出所有值  
}  

提前终止迭代

// 通过break/return/throw终止  
for await (const data of sensorStream) {  
  if (data.temperature  > 100) {  
    shutdownSystem();  
    break; // 停止监听  
  }  
}

与生成器结合

async function* genAsyncData() {  
  yield await fetch('//data1');  
  yield await fetch('//data2');  
}  
 
for await (const data of genAsyncData()) {  
  // 处理数据  
}  

与传统方案对比

需求Promise.all() 方案for await...of方案优势
按序处理异步任务❌(必须全部完成)✅(顺序处理)节省内存,即时处理
处理无限数据流不可能实现唯一解决方案
错误处理单个失败导致全部失败✅(可单独捕获)容错性强
动态添加任务✅(可配合数组追加)灵活扩展

牢记

for await...of = 异步世界的for...of,用同步语法遍历异步集合,数据随到随处理,不积压内存,随时中断/恢复迭代