Javascript Promise.all() 未解决或拒绝
Javascript Promise.all() not resolving or rejecting
给定以下代码:
let promiseArray = [];
fs.readdir('someFolderHere', (err, fileList) => {
filesList.forEach(filename => {
// readFile returns a promise that resolves
// to an array of strings to insert into the database
readFile(fileName).then((records) => {
records.forEach((record) => {
promiseArray.push(db.insert(record)); // db.insert returns a promise
});
});
});
// console.log(promiseArray); // empty array here???
Promise.all(promiseArray).then((res) => {
console.log('resolved');
resolve(res);
}).catch((err) => {
console.log('rejected');
reject(err);
});
});
我不明白为什么 promiseArray 没有在 Promise.all() 中解析。这些 console.log 语句都没有打印出来。
我猜测 Promise.all 在填充 promiseArray 之前被调用,因此它是一个空白数组,例如Promise.all([ ]).然后(...)
我该如何解决这个问题。我正在尝试读取文件列表,对于每个文件,我正在读取几行数据。我需要将所有文件中的所有记录都插入到数据库中——promises 只是不太好用。有没有办法让 Promise.all 等到 promiseArray 完全填充并且 forEach 完全完成?
您的猜测完全正确 — 您正在调用 Promise.all
使用空数组,因为您用于添加到数组的代码在尚未发生的实现回调中。
相反,它可能是这样的(见评论):
// Map the file list to an array of promises that will be fulfilled
// with an array of promises for the records.
const promiseArray = Promise.all(
// For each entry in `filesList`...
filesList.map(
// ...read the file and then...
filename => readFile(filename).then(
// ...take its records and insert them...
records => Promise.all(
records.map(
record => db.insert(record)
)
)
)
)
)
.then((res) => {
console.log('resolved');
resolve(res); // This is suspect, see below
}).catch((err) => {
console.log('rejected');
reject(err); // This is suspect, see below
});
resolve
和 reject
调用是可疑的,它们让我认为代码正在陷入显式承诺创建反模式。你已经有一个承诺(来自Promise.all
),所以你可以return它而不是return通过[=15=创建的承诺].
但是,除非这一切都在某种交易中,否则您可能希望使用 Promise.allSettled
而不是 Promise.all
这样您就可以了解什么成功了,什么没有成功,以防读取某些文件失败但其他文件有效。
一旦您停止使用 then
,您的代码将自动变得更短更清晰。示例:
let readFile = path => [path + ' record1', path + ' record2']
let insert = record => 'insert ' + record
async function test(fileList) {
let records = await Promise.all(fileList.map(path => readFile(path)))
return Promise.all(records.flat().map(rec => insert(rec)))
}
test(['A', 'B', 'C']).then(console.log);
这两行与您的 10 多行函数的作用相同。
作为对您评论的回应,您可以在使用“分解”地图时应用相同的通用模式,其中每个元素映射到一个数组(如文件夹 -> 每个文件夹中的文件,文件 -> 每个文件中的记录等)。
let folders = await Promise.all(serverList.map(server => getFolderOn(server)))
let files = await Promise.all(folders.flat().map(folder => getFilesIn(folder)))
let records = await Promise.all(files.flat().map(file => getRecordsIn(file)))
and so on...
您可以节省一些输入,并定义一个通用函数,例如
let flatPromise = (arrayOfArrays, mapper) =>
Promise.all(arrayOfArrays.flat().map(mapper))
然后
let folders = await flatPromise(serverList, server => getFolderOn(server))
let files = await flatPromise(folders, folder => getFilesIn(folder))
let records = await flatPromise(files, file => getRecordsIn(file))
....
给定以下代码:
let promiseArray = [];
fs.readdir('someFolderHere', (err, fileList) => {
filesList.forEach(filename => {
// readFile returns a promise that resolves
// to an array of strings to insert into the database
readFile(fileName).then((records) => {
records.forEach((record) => {
promiseArray.push(db.insert(record)); // db.insert returns a promise
});
});
});
// console.log(promiseArray); // empty array here???
Promise.all(promiseArray).then((res) => {
console.log('resolved');
resolve(res);
}).catch((err) => {
console.log('rejected');
reject(err);
});
});
我不明白为什么 promiseArray 没有在 Promise.all() 中解析。这些 console.log 语句都没有打印出来。
我猜测 Promise.all 在填充 promiseArray 之前被调用,因此它是一个空白数组,例如Promise.all([ ]).然后(...)
我该如何解决这个问题。我正在尝试读取文件列表,对于每个文件,我正在读取几行数据。我需要将所有文件中的所有记录都插入到数据库中——promises 只是不太好用。有没有办法让 Promise.all 等到 promiseArray 完全填充并且 forEach 完全完成?
您的猜测完全正确 — 您正在调用 Promise.all
使用空数组,因为您用于添加到数组的代码在尚未发生的实现回调中。
相反,它可能是这样的(见评论):
// Map the file list to an array of promises that will be fulfilled
// with an array of promises for the records.
const promiseArray = Promise.all(
// For each entry in `filesList`...
filesList.map(
// ...read the file and then...
filename => readFile(filename).then(
// ...take its records and insert them...
records => Promise.all(
records.map(
record => db.insert(record)
)
)
)
)
)
.then((res) => {
console.log('resolved');
resolve(res); // This is suspect, see below
}).catch((err) => {
console.log('rejected');
reject(err); // This is suspect, see below
});
resolve
和 reject
调用是可疑的,它们让我认为代码正在陷入显式承诺创建反模式。你已经有一个承诺(来自Promise.all
),所以你可以return它而不是return通过[=15=创建的承诺].
但是,除非这一切都在某种交易中,否则您可能希望使用 Promise.allSettled
而不是 Promise.all
这样您就可以了解什么成功了,什么没有成功,以防读取某些文件失败但其他文件有效。
一旦您停止使用 then
,您的代码将自动变得更短更清晰。示例:
let readFile = path => [path + ' record1', path + ' record2']
let insert = record => 'insert ' + record
async function test(fileList) {
let records = await Promise.all(fileList.map(path => readFile(path)))
return Promise.all(records.flat().map(rec => insert(rec)))
}
test(['A', 'B', 'C']).then(console.log);
这两行与您的 10 多行函数的作用相同。
作为对您评论的回应,您可以在使用“分解”地图时应用相同的通用模式,其中每个元素映射到一个数组(如文件夹 -> 每个文件夹中的文件,文件 -> 每个文件中的记录等)。
let folders = await Promise.all(serverList.map(server => getFolderOn(server)))
let files = await Promise.all(folders.flat().map(folder => getFilesIn(folder)))
let records = await Promise.all(files.flat().map(file => getRecordsIn(file)))
and so on...
您可以节省一些输入,并定义一个通用函数,例如
let flatPromise = (arrayOfArrays, mapper) =>
Promise.all(arrayOfArrays.flat().map(mapper))
然后
let folders = await flatPromise(serverList, server => getFolderOn(server))
let files = await flatPromise(folders, folder => getFilesIn(folder))
let records = await flatPromise(files, file => getRecordsIn(file))
....