在下面的代码中,我希望mark-1
在mark-2
之前触发。我猜我在某些地方没有正确使用await
和async
。
我很确定这一行:
const things = await fs.promises.readdir(folder);
和这一行
const stats = await fs.promises.stat(path);
是正确的,因为我们正在等待文件系统做出响应。
到目前为止,我还不了解错误检查或跨平台代码,只是获得了正确运行的承诺
// Libraries
const fs = require('fs');
// API
getThings('_t/').then(() => {
console.log('mark-2')
})
// Accesses the file system and returns an array of files / folders
async function getThings (folder) {
const things = await fs.promises.readdir(folder);
things.forEach(async (thing)=>{
await getStats(thing, folder);
});
}
// Gets statistics for each file/folder
async function getStats (thing, folder) {
const path = folder + thing;
const stats = await fs.promises.stat(path);
console.log('mark-1');
}
答案 0 :(得分:3)
问题是您在async
调用中使用了await
和forEach
,但这并不符合您的预期。
forEach
方法实际上并不关心回调函数的返回值(在这种情况下,getStats
返回的承诺)。
您应map
将things
数组用于promise,并使用Promise.all
:
async function getThings (folder) {
const things = await fs.promises.readdir(folder);
const promises = things.map(thing => getStats(thing, folder));
return await Promise.all(promises);
}
请注意,这将“并行”执行诺言,而不是顺序执行。
如果您想一步一步地安全地执行承诺,则可以“减少”承诺数组,也可以使用常规循环(for
,for-of
)。
编辑:
让我尝试说明为什么将异步回调函数与forEach一起使用不起作用。
在原始示例中,我们具有以下内容:
// ...
things.forEach(async (thing)=>{
await getStats(thing, folder);
});
// ...
如果我们将回调与forEach
分开,我们将:
const callback = async (thing)=>{
await getStats(thing, folder);
};
things.forEach(callback);
如果我们对异步功能进行“去糖化”:
const callback = function (thing) {
return new Promise((resolve, reject) => {
try {
getStats(thing, folder).then(stat => /*do nothing*/);
} catch (err) {
reject(err);
}
resolve();
});
};
things.forEach(callback);
用async
标记一个函数可确保该函数将始终返回一个承诺,无论其 completion 如何,如果该函数在没有显式返回值的情况下执行,承诺将得到解决。使用undefined
,如果它返回某个值,则promise将对其进行解析,最后,如果该函数中的某些内容引发抛出,则该Promise将被拒绝。
正如您所看到的,问题是承诺没有被任何东西等待,而且它们也没有解决任何价值。实际上,在回调函数中放置的await对该值不执行任何操作,就像上面我在做.then
并且对该值不执行任何操作一样。
forEach
函数:
function forEach(array, callback) {
for(const value of array) {
callback(value);
}
}
const values = [ 'a', 'b', 'c' ];
forEach(values, (item) => {
console.log(item)
});
上面的forEach是Array.prototype.forEach
方法的简化,只是为了显示其结构,实际上,回调函数被称为将数组作为this
值传递,并传递三个参数,即当前元素,当前索引以及数组实例,但我们明白了。
如果我们想实现一个async
forEach
函数,我们将不得不等待回调调用:
const sleep = (time, value) => new Promise(resolve => setTimeout(resolve(value), time));
const values = [ { time: 300, value: 'a'}, { time: 200, value: 'b' }, {time: 100, value: 'c' } ];
async function forEachAsync(array, callback) {
for(const value of array) {
await callback(value);
}
}
(async () => {
await forEachAsync(values, async (item) => {
console.log(await sleep(item.time, item.value))
});
console.log('done');
})()
上面的forEachAsync
函数将依次逐项进行迭代和等待,通常您不希望这样做,如果异步函数是独立的,则可以并行完成它们,就像我首先建议的那样
const sleep = (time, value) => new Promise(resolve => setTimeout(resolve(value), time));
const values = [ { time: 300, value: 'a'}, { time: 200, value: 'b' }, {time: 100, value: 'c' } ];
(async () => {
const promises = values.map(item => sleep(item.time, item.value));
const result = await Promise.all(promises);
console.log(result);
})()
正如您所看到的,即使promise是并行执行的,我们也会以与数组中promise相同的顺序获得结果。
但是此示例与第一个示例的区别在于,该示例仅花费300毫秒(解决问题的最长承诺),而第一个示例则花费了600毫秒(300毫秒+ 200毫秒+ 100毫秒)。
希望它变得更清晰