Javascript - 所有嵌套forEach循环完成后的回调
Javascript - Callback after all nested forEach loops are completed
我确信这是一项相当简单的任务,但我现在无法全神贯注。我有一组嵌套的 forEach 循环,当所有循环完成时我需要一个回调 运行。
我愿意使用 async.js
这就是我正在使用的:
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
//do something
//add file to jobs array
jobs.push(file);
});
});
});
});
//return jobs array once all files have been added
cb(jobs);
}
您可以使用walk
walker.on("end", function () {
console.log("all done");
cb(jobs);
});
使用forEach
的第二个参数,索引,你可以在每次运行最内层循环时检查是否所有循环都完成了。
因此,只需将几行代码添加到您的代码中,您就会得到:
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function(account, accIndex) {
dirs.forEach(function(dir, dirIndex) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file, fileIndex) {
//do something
//add file to jobs array
jobs.push(file);
// Check whether each loop is on its last iteration
const filesDone = fileIndex >= files.length - 1;
const dirsDone = dirIndex >= dirs.length - 1;
const accsDone = accIndex >= accounts.length - 1;
// all three need to be true before we can run the callback
if (filesDone && dirsDone && accsDone) {
cb(jobs);
}
});
});
});
});
}
所以问题是您在 fs.readdir
完成之前发送了一个空结果,因为 nodeJS 是异步的。所以解决方案是在 fs.readdir 函数中添加回调。
const scanFiles = function (accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function (account, i) {
dirs.forEach(function (dir, j) {
fs.readdir(account + '/' + dir, function (err, files) {
files.forEach(function (file, k) {
//do something
//add file to jobs array
jobs.push(file);
});
if (i === accounts.length - 1 && j === dirs.length - 1 && k === files.length - 1) {
//return jobs array once all files have been added
cb(jobs);
}
});
});
});
}
简单计数器
一个简单的方法就是保留一个计数器。
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
// Variables to keep track of
const lastAccountIndex = accounts.length * dirs.length;
let indexCounter = 0;
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
//do something
//add file to jobs array
jobs.push(file);
indexCounter++;
});
//return jobs array once all files have been added
if (lastAccountIndex === indexCounter) {
cb(jobs);
}
});
});
});
}
承诺
或者,fs + promise 在这里可能非常有用。
const scanFiles = function(accounts) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
const filePromises = [];
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
filePromises.push(new Promise((resolve, reject) => {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
resolve(file);
});
});
}));
});
});
return Promise.all(filePromises);
}
scanFiles(someAccounts)
.then((files) => {
files.forEach((file) => {
// At this point, iwll the files will be scanned
// So, do whatever you want with all the files here.
});
});
fs-承诺
更简单的解决方案
不需要循环和推送到数组
我注意到这里的所有答案都使用了很多复杂的代码。
你可以让它更简单:
let fs = require('mz/fs');
let path = require('path');
let d = ['pending', 'done', 'failed'];
let a = ['A', 'B', 'C']; // <-- example accounts
let paths = [].concat.apply([], d.map(d => (a.map(a => path.join(d,a)))));
Promise.all(paths.map(path => fs.readFile(path, 'utf-8'))).then(files => {
// you have all data here
}).catch(error => {
// handle errors here
});
说明
如果您使用 fs
的 promise 版本 - 目前您可以使用:
let fs = require('mz/fs');
使用 mz
模块:
很快它就会成为 Node 的原生版本,请参阅:
然后你就可以像下面的代码那样做。使用数据:
// directories:
let d = ['pending', 'done', 'failed'];
// accounts:
let a = ['A', 'B', 'C'];
您可以轻松创建路径数组:
let paths = [].concat.apply([], d.map(d => (a.map(a => path.join(d,a)))));
您可以从中创建一个承诺数组:
let promises = paths.map(path => fs.readFile(path, 'utf-8'));
您甚至可以使用 Promise.all()
读取所有文件:
let data = Promise.all(promises);
现在您可以将所有内容用作:
data.then(files => {
// you have everything ready here
}).catch(error => {
// some error happened
});
注意:以上代码需要两个模块才能运行:
let fs = require('mz/fs');
let path = require('path');
如果你使用异步库 https://caolan.github.io/async/docs.html, your code will be much faster. (forEach is blocking [JavaScript, Node.js: is Array.forEach asynchronous?)。
const scanFiles = function (accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
async.each(accounts, function (account, accountCallback) {
async.each(dirs, function (dir, dirCallback) {
fs.readdir(account + '/' + dir, function (err, files) {
if(err) console.log(err);
async.each(files, function (file, fileCallback) {
//do something
//add file to jobs array
jobs.push(file);
fileCallback();
}, dirCallback);
});
}, accountCallback);
}, function (err) {
//return jobs array once all files have been added
if (err) throw err;
cb(jobs)
});
};
我确信这是一项相当简单的任务,但我现在无法全神贯注。我有一组嵌套的 forEach 循环,当所有循环完成时我需要一个回调 运行。
我愿意使用 async.js
这就是我正在使用的:
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
//do something
//add file to jobs array
jobs.push(file);
});
});
});
});
//return jobs array once all files have been added
cb(jobs);
}
您可以使用walk
walker.on("end", function () {
console.log("all done");
cb(jobs);
});
使用forEach
的第二个参数,索引,你可以在每次运行最内层循环时检查是否所有循环都完成了。
因此,只需将几行代码添加到您的代码中,您就会得到:
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function(account, accIndex) {
dirs.forEach(function(dir, dirIndex) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file, fileIndex) {
//do something
//add file to jobs array
jobs.push(file);
// Check whether each loop is on its last iteration
const filesDone = fileIndex >= files.length - 1;
const dirsDone = dirIndex >= dirs.length - 1;
const accsDone = accIndex >= accounts.length - 1;
// all three need to be true before we can run the callback
if (filesDone && dirsDone && accsDone) {
cb(jobs);
}
});
});
});
});
}
所以问题是您在 fs.readdir
完成之前发送了一个空结果,因为 nodeJS 是异步的。所以解决方案是在 fs.readdir 函数中添加回调。
const scanFiles = function (accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
accounts.forEach(function (account, i) {
dirs.forEach(function (dir, j) {
fs.readdir(account + '/' + dir, function (err, files) {
files.forEach(function (file, k) {
//do something
//add file to jobs array
jobs.push(file);
});
if (i === accounts.length - 1 && j === dirs.length - 1 && k === files.length - 1) {
//return jobs array once all files have been added
cb(jobs);
}
});
});
});
}
简单计数器
一个简单的方法就是保留一个计数器。
const scanFiles = function(accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
// Variables to keep track of
const lastAccountIndex = accounts.length * dirs.length;
let indexCounter = 0;
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
//do something
//add file to jobs array
jobs.push(file);
indexCounter++;
});
//return jobs array once all files have been added
if (lastAccountIndex === indexCounter) {
cb(jobs);
}
});
});
});
}
承诺
或者,fs + promise 在这里可能非常有用。
const scanFiles = function(accounts) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
const filePromises = [];
accounts.forEach(function(account) {
dirs.forEach(function(dir) {
filePromises.push(new Promise((resolve, reject) => {
fs.readdir(account + '/' + dir, function(err, files) {
files.forEach(function(file) {
resolve(file);
});
});
}));
});
});
return Promise.all(filePromises);
}
scanFiles(someAccounts)
.then((files) => {
files.forEach((file) => {
// At this point, iwll the files will be scanned
// So, do whatever you want with all the files here.
});
});
fs-承诺
更简单的解决方案
不需要循环和推送到数组
我注意到这里的所有答案都使用了很多复杂的代码。 你可以让它更简单:
let fs = require('mz/fs');
let path = require('path');
let d = ['pending', 'done', 'failed'];
let a = ['A', 'B', 'C']; // <-- example accounts
let paths = [].concat.apply([], d.map(d => (a.map(a => path.join(d,a)))));
Promise.all(paths.map(path => fs.readFile(path, 'utf-8'))).then(files => {
// you have all data here
}).catch(error => {
// handle errors here
});
说明
如果您使用 fs
的 promise 版本 - 目前您可以使用:
let fs = require('mz/fs');
使用 mz
模块:
很快它就会成为 Node 的原生版本,请参阅:
然后你就可以像下面的代码那样做。使用数据:
// directories:
let d = ['pending', 'done', 'failed'];
// accounts:
let a = ['A', 'B', 'C'];
您可以轻松创建路径数组:
let paths = [].concat.apply([], d.map(d => (a.map(a => path.join(d,a)))));
您可以从中创建一个承诺数组:
let promises = paths.map(path => fs.readFile(path, 'utf-8'));
您甚至可以使用 Promise.all()
读取所有文件:
let data = Promise.all(promises);
现在您可以将所有内容用作:
data.then(files => {
// you have everything ready here
}).catch(error => {
// some error happened
});
注意:以上代码需要两个模块才能运行:
let fs = require('mz/fs');
let path = require('path');
如果你使用异步库 https://caolan.github.io/async/docs.html, your code will be much faster. (forEach is blocking [JavaScript, Node.js: is Array.forEach asynchronous?)。
const scanFiles = function (accounts, cb) {
let dirs = ['pending', 'done', 'failed'];
let jobs = [];
async.each(accounts, function (account, accountCallback) {
async.each(dirs, function (dir, dirCallback) {
fs.readdir(account + '/' + dir, function (err, files) {
if(err) console.log(err);
async.each(files, function (file, fileCallback) {
//do something
//add file to jobs array
jobs.push(file);
fileCallback();
}, dirCallback);
});
}, accountCallback);
}, function (err) {
//return jobs array once all files have been added
if (err) throw err;
cb(jobs)
});
};