如何处理异步循环?
How to handle async loop?
我目前正在开发一个项目,基于 lunr.js 在 JavaScript 中实现全文搜索客户端。
问题是,我在构建然后保存索引时遇到了困难,因为我有几个异步调用。
function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
var path = path || require('path');
var fs = fs || require('fs'),
promesses = [],
ignore = ['node_modules'],
files = fs.readdirSync(root);
files.forEach(function (file) {
if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
buildIndex(rawIndex, path.join(root, file), indexPath);
}
else if (file.substr(-5) === '.html' && file != 'example.html') {
var promesse = JSDOM.fromFile(path.join(root, file)).then(dom => {
var $ = require('../lib/_jquery')(dom.window);
populate();
console.log(file + " indexé");
function populate() {
$('h1, h2, h3, h4, h5, h6').each(function () {
var title = $(this);
var link = path.join(root, file).replace('..\', '') + "#" + title.prop('id');
var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
rawIndex.add({
id: link,
title: title.text().latinise(),
body: body.text().latinise()
});
});
};
});
promesses.push(promesse);
}
});
Promise.all(promesses)
.then(function () {
fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
})
.catch(function (err) {
console.log("Failed:", err);
});
};
提前致谢。
有四个问题:
- 您的函数
buildIndex
没有 return
承诺,因此调用它时不能等待结果
- 当遇到一个目录时,你递归地调用
buildIndex
但不要像在其他情况下使用 promesse
那样等待它的结果。
- 你在异步回调中有
promesses.push(promesse);
调用,它只在文件被读入后执行。将 promise 放在数组中的想法是正确的,但你必须立即这样做才能发生在数组上调用 Promise.all
之前。
- 您出于某种原因从代码中删除了
Promise.all
。
基本上该函数应该具有以下一般架构:
function buildIndex(…) {
…
var promises = paths.map(function(path) {
if (isDir(path)) {
return buildIndex(…);
} else {
return JSDOM.fromFile(…).then(…);
}
});
return Promise.all(promises).then(…);
}
使用 forEach 并不是一个想要 return Promise 的正确选择。
因此,在 if/else 语句中使用 .map 然后使用 return Promise 是更明智的做法。
最后,必须调用 Promises.all(promises) 使 .then(...) 可以按预期使用。
我的最终函数:
function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
var path = path || require('path');
var fs = fs || require('fs'),
promises = [],
ignore = ['node_modules'],
files = fs.readdirSync(root);
var promises = files.map(function (file) {
if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
return buildIndex(rawIndex, path.join(root, file), indexPath);
}
else if (file.substr(-5) === '.html' && file != 'example.html') {
return JSDOM.fromFile(path.join(root, file)).then(dom => {
var $ = require('jquery')(dom.window);
populate();
console.log(file + " indexé");
function populate() {
$('h1, h2, h3, h4, h5, h6').each(function () {
var title = $(this);
var link = path.join(root, file).replace('..\', '') + "#" + title.prop('id');
var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
rawIndex.add({
id: link,
title: title.text().latinise(),
body: body.text().latinise()
});
});
};
})
}
})
return Promise.all(promises).then(function () {
fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
});
};
感谢@Bergi 的回答和帮助的人。
我目前正在开发一个项目,基于 lunr.js 在 JavaScript 中实现全文搜索客户端。
问题是,我在构建然后保存索引时遇到了困难,因为我有几个异步调用。
function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
var path = path || require('path');
var fs = fs || require('fs'),
promesses = [],
ignore = ['node_modules'],
files = fs.readdirSync(root);
files.forEach(function (file) {
if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
buildIndex(rawIndex, path.join(root, file), indexPath);
}
else if (file.substr(-5) === '.html' && file != 'example.html') {
var promesse = JSDOM.fromFile(path.join(root, file)).then(dom => {
var $ = require('../lib/_jquery')(dom.window);
populate();
console.log(file + " indexé");
function populate() {
$('h1, h2, h3, h4, h5, h6').each(function () {
var title = $(this);
var link = path.join(root, file).replace('..\', '') + "#" + title.prop('id');
var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
rawIndex.add({
id: link,
title: title.text().latinise(),
body: body.text().latinise()
});
});
};
});
promesses.push(promesse);
}
});
Promise.all(promesses)
.then(function () {
fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
})
.catch(function (err) {
console.log("Failed:", err);
});
};
提前致谢。
有四个问题:
- 您的函数
buildIndex
没有return
承诺,因此调用它时不能等待结果 - 当遇到一个目录时,你递归地调用
buildIndex
但不要像在其他情况下使用promesse
那样等待它的结果。 - 你在异步回调中有
promesses.push(promesse);
调用,它只在文件被读入后执行。将 promise 放在数组中的想法是正确的,但你必须立即这样做才能发生在数组上调用Promise.all
之前。 - 您出于某种原因从代码中删除了
Promise.all
。
基本上该函数应该具有以下一般架构:
function buildIndex(…) {
…
var promises = paths.map(function(path) {
if (isDir(path)) {
return buildIndex(…);
} else {
return JSDOM.fromFile(…).then(…);
}
});
return Promise.all(promises).then(…);
}
使用 forEach 并不是一个想要 return Promise 的正确选择。 因此,在 if/else 语句中使用 .map 然后使用 return Promise 是更明智的做法。 最后,必须调用 Promises.all(promises) 使 .then(...) 可以按预期使用。
我的最终函数:
function buildIndex(rawIndex, root, indexPath = root + 'js/app/index.json') {
var path = path || require('path');
var fs = fs || require('fs'),
promises = [],
ignore = ['node_modules'],
files = fs.readdirSync(root);
var promises = files.map(function (file) {
if (fs.statSync(path.join(root, file)).isDirectory() && ignore.indexOf(file) == -1) {
return buildIndex(rawIndex, path.join(root, file), indexPath);
}
else if (file.substr(-5) === '.html' && file != 'example.html') {
return JSDOM.fromFile(path.join(root, file)).then(dom => {
var $ = require('jquery')(dom.window);
populate();
console.log(file + " indexé");
function populate() {
$('h1, h2, h3, h4, h5, h6').each(function () {
var title = $(this);
var link = path.join(root, file).replace('..\', '') + "#" + title.prop('id');
var body = title.nextUntil('h1, h2, h3, h4, h5, h6');
rawIndex.add({
id: link,
title: title.text().latinise(),
body: body.text().latinise()
});
});
};
})
}
})
return Promise.all(promises).then(function () {
fs.writeFileSync(indexPath, "var data = " + JSON.stringify(rawIndex), 'utf8');
});
};
感谢@Bergi 的回答和帮助的人。