【问题标题】:Javascript Promise.all() not resolving or rejectingJavascript Promise.all() 没有解决或拒绝
【发布时间】:2021-08-13 00:22:50
【问题描述】:

给定以下代码:

                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([ ]).then(...)

我该如何解决这个问题。我正在尝试读取文件列表,对于每个文件,我正在读取几行数据。我需要将所有文件中的所有记录都插入到数据库中——promise 并不是很好。有没有办法让 Promise.all 等到 promiseArray 完全填充并且 forEach 完全完成?

【问题讨论】:

  • Promise.all(promiseArray) 在任何promiseArray.push(db.insert(record)); 被执行之前被调用
  • @DraganS 是的,我很怀疑......我该如何绕过它?
  • 这取决于要求。你将如何处理错误。你会更新 db 还是只有在所有文件都设法加载记录时才会更新 db。
  • 使用for (const p of promises) ..... => 这让一个等待下一个......

标签: javascript asynchronous ecmascript-6 promise es6-promise


【解决方案1】:

一旦你停止使用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))
....

【讨论】:

  • 您应该使用.map(f => readFile(f)),因为.map 会将意外参数传递给readFile
  • 这应该是正确答案。不确定是否可以将 flatMap 替换为这里的 map 方法。 ``` async function a () { const filesList = ['a', 'b', 'c'] let records = await Promise.all(filesList.map((file) => Promise.resolve([file, file ]))) return Promise.all(records.map(rec => Promise.resolve(rec))) } a().then(x => console.log(x)); ``
  • @DraganS:对,应该是flat().map。已更新。
  • 谢谢,感谢 - 我现在也不得不遍历一个文件夹,这是围绕代码的另一个承诺。是否也可以将其包含在您的测试功能中?
【解决方案2】:

您的猜测完全正确——您使用空数组调用 Promise.all,因为您用于添加到数组的代码位于尚未发生的实现回调中。

相反,它可能是这样的(参见 cmets):

// 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 创建反模式。你已经有一个承诺(来自Promise.all),所以你可以直接返回它而不是返回一个通过new Promise创建的承诺。

但是,除非这一切都在某种事务中,否则您可能希望使用Promise.allSettled 而不是Promise.all,这样您就可以了解什么成功和失败了,在案例读取某些文件失败但其他文件有效。

【讨论】:

    猜你喜欢
    • 2017-07-29
    • 1970-01-01
    • 2023-03-26
    • 2022-08-20
    • 1970-01-01
    • 2015-10-09
    • 2019-08-06
    • 1970-01-01
    • 2021-12-20
    相关资源
    最近更新 更多