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
});

resolvereject 调用是可疑的,它们让我认为代码正在陷入显式承诺创建反模式。你已经有一个承诺(来自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))
....