Node.js新手求助:foreach循环中输出顺序为何与预期相反?
为什么输出顺序是
ciao data data而非预期的data data ciao? 嘿,我来帮你把这个问题掰明白~核心原因其实是Node.js的异步I/O和事件循环机制,跟forEach本身是不是异步关系不大,咱们一步步拆解:
你的代码执行流程是这样的:
- 首先
fs.readdir的回调函数开始执行,里面先同步完成了fs.readFileSync和JSON.parse,这部分是阻塞的,没问题。 - 接下来进入
files.forEach循环——没错,forEach是同步遍历,它会立刻把所有文件的fs.stat调用都发起,但!fs.stat是异步I/O操作,它不会等当前文件的stat结果返回再处理下一个,而是把每个stat的回调函数放到Node.js的事件队列里,自己继续往下走。 - 当
forEach遍历完所有文件之后,会立刻执行下面的console.log("ciao")——这时候所有fs.stat的回调都还没执行呢,因为异步I/O需要等操作系统返回文件状态的结果,才会把回调从事件队列里拿出来执行。 - 等当前同步代码(也就是
readdir回调里除了异步回调之外的部分)全部执行完,Node.js的事件循环才会开始处理事件队列里的fs.stat回调,这时候才会陆续打印出data。
所以最终的输出顺序就是先ciao,再两个data啦~
怎么修改才能得到预期的data data ciao?
你可以用async/await配合Promise.all来控制异步操作的顺序,把异步的fs.stat操作变成可以等待的Promise:
const fs = require('fs').promises; // 使用Promise版本的fs模块 const path = require('path'); fs.readdir("sender").then(async (files) => { const dirPath = path.join(__dirname, "sender"); // 用await替代同步的readFileSync,更符合异步风格 const rawdata = await fs.readFile(path.join(dirPath, "data.json")); const data = JSON.parse(rawdata); // 把每个文件的stat操作包装成Promise,用Promise.all等待所有完成 await Promise.all(files.map(async (file) => { const stats = await fs.stat(path.join(dirPath, file)); if (data[file]["size"] !== stats.size) data[file]["size"] = stats.size; if (data[file]["time"] !== stats.mtime.toISOString()) data[file]["time"] = stats.mtime.toISOString(); console.log("data"); })); // 所有异步操作完成后再打印ciao console.log("ciao"); }).catch(err => { console.error(err); });
这个修改的核心是:
- 用
fs.promises提供的Promise版本方法,替代回调风格的fs.stat和fs.readFile - 用
files.map生成一个包含所有stat操作Promise的数组,再用Promise.all等待所有Promise都resolve(也就是所有stat操作都完成) - 只有等所有
data都打印完,才会执行console.log("ciao")
内容的提问来源于stack exchange,提问作者tommyzan
相关产品推荐
相关产品推荐

