【问题标题】:.then of Promise.all not executing.then 的 Promise.all 没有执行
【发布时间】:2018-07-25 21:01:06
【问题描述】:

我正在使用 Firestore 检索具有以下 DS 的数据。
我有一个 Company 集合,其中包含一个子集合 Branches
所以我试图检索列出所有 Companies 及其 Branches

代码:

exports.findAll = function (req, res) {
    getcompanies().
    then((companies) => {
        console.log("Main "+ companies) // info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined
        return res.json(companies);
    })
    .catch((err) => {
        console.log('Error getting documents', err);
    });
}

function getCompanies(){
    var companiesRef = db.collection('companies');

    return companiesRef.get()
    .then((snapshot) => {
        let companies = [];
        return Promise.all(
            snapshot.forEach(doc => {  
                    let company = {};                
                    company.id = doc.id;
                    company.company = doc.data(); 
                    var branchesPromise = getBranchesForCompanyById(company.id);
                    return branchesPromise.then((branches) => {                    
                            company.branches = branches;
                            companies.push(company); 
                            if(snapshot.size === companies.length){
                                console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches
                            }
                            return Promise.resolve(companies);
                        })
                        .catch(err => {
                            console.log("Error getting sub-collection documents", err);
                            return Promise.reject(err);
                        }) 
            })
        )
        .then(companies => {
            console.log("Outside " + companies) // This is never executed 
            return companies;
        })
        .catch(err => {
            return err;
        });

    })
    .catch(err => {
        return err;
    });
}

function getBranchesForCompanyById(id){
    var branchesRef = db.collection('companies').doc(id).collection('branches');
    let branches = [];
    return branchesRef.get()
     .then(snapshot => {
        snapshot.forEach(brnch => {
            let branch = {};
            branch.id = brnch.id;
            branch.branch = brnch.data();
            branches.push(branch);
        })
        return branches;
    })
    .catch(err => {
        return err;
    })

 }

我现在已经有了所有需要的数据。

console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches

但是 Promise.all 的 then 永远不会被执行。所以得到这个错误 -

info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

console.log("Main "+ companies) // info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

我觉得我已经遵循了这里指定的所有规则:https://stackoverflow.com/a/31414472/2114024 关于嵌套承诺,不知道我在哪里漏掉了重点。
提前致谢!

【问题讨论】:

    标签: node.js promise google-cloud-firestore


    【解决方案1】:

    根据 Evert 和 Rahul 的意见,感谢你们两位,我已经解决了这里的问题。

    1. 我处理了 catch 块中的所有错误
    2. Promise.all 没有返回任何内容,因此我将 forEach 转换为 map。

    所以这是我更新的代码,它解决了问题:

    exports.findAll = function (req, res) {
        getcompanies().
            then((companies) => {
                console.log("Main " + companies) // Prints all companies with its branches
                return res.json(companies);
            })
            .catch((err) => {
                console.log('Error getting documents', err);
                return res.status(500).json({ message: "Error getting the all companies" + err });
            });
    }
    
    function getCompanies() {
        var companiesRef = db.collection('companies');
    
        return companiesRef.get()
            .then((snapshot) => {
                let companies = [];
                return Promise.all(
                    snapshot.docs.map(doc => {
                        let company = {};
                        company.id = doc.id;
                        company.company = doc.data();
                        var branchesPromise = getBranchesForCompanyById(company.id);
                        return branchesPromise.then((branches) => {
                            company.branches = branches;
                            companies.push(company);
                            if (snapshot.size === companies.length) {
                                console.log("companies - Inside" + JSON.stringify(companies));
                                return companies;
                            }
                        })
                            .catch(err => {
                                console.log("Error getting sub-collection documents", err);
                                throw new Error(err);
                            })
                    })
                )
                    .then(companies => {
                        console.log("Outside " + companies); // Executed now
                        return companies[companies.length - 1];
                    })
                    .catch(err => {
                        throw new Error(err);
                    });
    
            })
            .catch(err => {
                throw new Error(err);
            });
    }
    
    function getBranchesForCompanyById(id) {
        var branchesRef = db.collection('companies').doc(id).collection('branches');
        let branches = [];
        return branchesRef.get()
            .then(snapshot => {
                snapshot.forEach(brnch => {
                    let branch = {};
                    branch.id = brnch.id;
                    branch.branch = brnch.data();
                    branches.push(branch);
                })
                return branches;
            })
            .catch(err => {
                throw new Error(err);
            })
    
    }
    

    【讨论】:

    • SO 应该允许您接受自己的答案。这样做是完全可以接受的。
    【解决方案2】:

    在您的代码中,您可以使用 map 而不是 forEach。 Promise.all 接受一个承诺数组,但 forEach 不返回一个数组

    return Promise.all(
        snapshot.map(doc => {
            let company = {};
            company.id = doc.id;
            company.company = doc.data();
            var branchesPromise = getBranchesForCompanyById(company.id);
            return branchesPromise.then((branches) => {
                    company.branches = branches;
                    companies.push(company);
                    if (snapshot.size === companies.length) {
                        console.log("companies - Inside" + JSON.stringify(companies)); //This prints all companies with its branches
                    }
                    return Promise.resolve(companies);
                })
                .catch(err => {
                    console.log("Error getting sub-collection documents", err);
                    return Promise.reject(err);
                })
        })
    )
    

    【讨论】:

    • 我试过了,我认为,快照不支持地图。会再试一次看看。谢谢!
    • 不需要返回 Promise.resolve(companies);您可以直接返回公司
    【解决方案3】:

    我发现至少有 2 个问题:

    • forEach 可能不会返回任何内容,您将 forEach 的结果发送到 Promise.all()
    • 如果 Promise.all() 抛出异常,您的一些 catch 处理程序只会抓取错误并返回它。归还它会将其变为非异常。

    您实际上也不必为每个 Promise 链添加 catch,只要将 Promise 链的结果反馈到另一个 Promise 链,您可能只需要 1 个 catch 块。

    您的then() 函数之一也不应该嵌套得那么深。只需将其提升一个级别,这就是承诺的意义所在。

    【讨论】:

    • 感谢您的意见。我已经更新了问题,我得到了一个 -info: Main TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined。我仍然会根据您的输入重构代码。再次感谢!
    • _你也真的不必为每一个 Promise 链添加一个 catch,只要你将一个 Promise 链的结果反馈给另一个 Promise 链,你可能只需要 1 个 catch 块_回复为此:Firestore 强制处理每个 promise 调用。
    • 如果你在后面有一个 catch 块,你仍然会处理每个 promise 调用。我绝对同意每个 Promise 链必须有一个 catch 块,但只要你不断返回 Promise 的结果,你最终只需要一个。这绝对是可能的情况之一。;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2019-09-28
    • 2016-01-09
    • 2018-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多