为什么Node.js会这样执行?
问题:为什么Node.js会这样执行? 我有一个 Node.js 应用程序,用于将记录从 MySql 迁移到 MongoDB。我正在使用 Mongoose 和 async.js 来执行此操作,并且我注意到一些我不理解的行为。如果我有以下 Coffeescript 代码(此处为javascript): # users is a collection of about 70k records asyn
问题:为什么Node.js会这样执行?
我有一个 Node.js 应用程序,用于将记录从 MySql 迁移到 MongoDB。我正在使用 Mongoose 和 async.js 来执行此操作,并且我注意到一些我不理解的行为。如果我有以下 Coffeescript 代码(此处为javascript):
# users is a collection of about 70k records
async.each users, ((user, callback) =>
# console.log "saving user: #{user.id} of #{users[users.length-1].id}"
model = new User
id: user.id
name:
first: user.fname
last: user.lname
model.save (err) ->
console.log "saving user: #{user.id}"
model = null
callback(err)
), (err) ->
users = null
callback(err)
从未达到model.save
的回调,我的 Node 进程将慢慢爬升至 1.5gb。如果我检查我的 mongodb 实例,我可以看到在处理完users
集合中的所有 70k 项之后,记录将开始保存到 mongodb,但它们停止在 41k 左右。
我注意到,如果我从async.each切换到async.eachSeries,则每条记录都会调用model.save
并且迁移成功完成。
我假设由于某种原因,Node 在执行model.save
的回调之前,针对users
集合中的每个项目运行 async.each 的每次迭代,这会导致内存问题,但我不明白为什么会这样.谁能告诉我为什么 Node 会这样做,为什么切换到async.eachSeries
可以解决这个问题?
解答
尼尔在提供解决方案方面做得很好,但我只是想谈谈你的问题:
谁能告诉我为什么 Node 会这样做,以及为什么切换到 async.eachSeries 可以解决这个问题?
如果您查看async.each
与async.eachSeries
的详细信息,您可能会注意到async.each
的文档指出:
将函数迭代器并行应用于 arr 中的每个项目
但是,async.eachSeries
指出:
与 each 相同,仅迭代器应用于 arr 中的每个项目。仅在当前迭代器完成后才调用下一个迭代器。这意味着迭代器函数将按顺序完成。
详细来说,如果我们查看代码,您会发现each
的代码最终调用了数组本身的原生forEach
函数,并且每个元素都调用了迭代器(指向源代码的链接):
_each(arr, function (x) {
iterator(x, only_once(done) );
});
调用:
var _each = function (arr, iterator) {
if (arr.forEach) {
return arr.forEach(iterator);
}
但是,对迭代器函数的每次调用最终都会调用model.save
。这个 Mongoose 函数(除其他外)最终会执行 I/O 以将数据保存到数据库中。如果您要跟踪代码路径,您会看到它最终出现在一个调用process.nextTick
的函数中(链接到源代码)。
Node 的process.nextTick
函数通常用于这种情况(I/O),一旦执行流程结束就会处理回调。在这种情况下,只有在 forEach 循环完成后才会调用每个回调。 (这是有目的的,并且不会阻止任何代码执行。)
所以总结一下:
使用async.each
时,您上面的代码将遍历所有用户,将保存排队,但只有在代码完成对所有用户的迭代后才开始处理它们。
使用async.eachSeries
时,您上面的代码将一次处理每个用户,并且仅在保存完成后处理下一个用户 - 当调用 eachSeries 回调时。
更多推荐
所有评论(0)