【问题标题】:Wait for the result of forEach before sending callback to main在向 main 发送回调之前等待 forEach 的结果
【发布时间】:2019-08-06 05:16:22
【问题描述】:

我想收集数据及其子项(绑定到该产品 ID 的另一项) 我有一个具有此架构的集合

// User Schema
const filtersSchema = mongoose.Schema({
    filter_name:{
        type: String,
        required: true
    },
    filter_code:{
        type: String,
        required: true,
        unique: true
    },
    bind_to: {
        type: Schema.Types.ObjectId,
        default: null
    },
    filter_status:{
        type: Boolean,
        default: true
    },
    created_on:{
        type: Date,
        default: Date.now
    },
    updated_on:{
        type: Date,
        default: Date.now
    }
});

如果我向它输入数据,默认的 bind_to 值将为空,这意味着它的父级。如果我发送父级的 bind_to ID,它将是 ObjectID。

我想收集这样的数据

[{
-- parent object --
children:[
  {
  -- child object --
  },
  {
  -- child object --
  }
]
}]

如果我们有多个项目,它将通过循环 (forEach),但回调是在 forEach 循环完成之前发送的。我知道 forEach 是异步的,请求是同步的。但对如何做到这一点感到困惑!

你可以看到下面的模块

// Get Filters by Parent ID
module.exports.getFiltersByParentId = (pid, callback) => {
    Filters.find({bind_to: pid}, callback);
}

//For getting the parent object and looping it to get its child objects
module.exports.getFilters = (callback, limit) => {
    Filters.find({bind_to: null}, (err, filters) => {
        if (err) {
            console.log(err);
            let obj = {status: false, error: err.errmsg};
            callback(obj);
        } else {
            const resObj = [];
            filters.forEach(async function (ele) {
                await Filters.getFiltersByParentId(ele._id, (err, cfil) => {
                    if (err) {
                        let obj = {status: false, message: err.errmsg};
                        callback(obj);
                    } else {
                        console.log(ele, "Obj");
                        ele.children = cfil;
                        resObj.push(ele);
                    }
                });
            });
            Promise.all(resObj).then(res => {
                let obj = {status: true, data: res, message: "Filters found"};
                callback(obj);
            });
        }
    });
}

但在这种情况下,结果对象将为空。如何获得具有上述值的正确对象?

即使我尝试过这种方法

const resObj = [];
            filters.map(function (ele) {
                Filters.getFiltersByParentId(ele._id, (err, cfil) => {
                    if (err) {
                        let obj = {status: false, message: err.errmsg};
                        callback(obj);
                    } else {
                        console.log(ele, "Obj");
                        ele.children = cfil;
                        resObj.push(ele);
                    }
                });
            });
            Promise.all(resObj).then(res => {
                let obj = {status: true, data: res, message: "Filters found"};
                callback(obj);
            });

还有这个

Promise.all(filters.map(function (ele) {
                Filters.getFiltersByParentId(ele._id, (err, cfil) => {
                    if (err) {
                        let obj = {status: false, message: err.errmsg};
                        callback(obj);
                    } else {
                        console.log(ele, "Obj");
                        ele.children = cfil;
                        resObj.push(ele);
                    }
                });
            })).then(res => {
                let obj = {status: true, data: res, message: "Filters found"};
                callback(obj);
            });

好的,现在我从getFiltersByParentId返回一个承诺

module.exports.getFiltersByParentId = (pid, callback) => {
    return new Promise(function(resolve, reject) {
        Filters.find({bind_to: pid}, function (err, results) {
            if (err) {
                reject(err);
            } else {
                resolve(results);
            }
        })
    });
}

【问题讨论】:

  • I know forEach is asynchronous and the request is synchronous 正好相反。
  • 您正在尝试在 forEach 中使用 await - forEach 行将在异步操作完成之前完成,因此 resObj 为空。请改用.map
  • 你需要返回一个Promise,现在映射的数组只是undefineds,查看如何将回调API转换为promise
  • 应该callback(obj)if (err) {块中终止处理,还是callback在错误和成功的情况下期望被多次调用

标签: node.js express mongoose promise


【解决方案1】:

首先,由于Filters.getFiltersByParentId 没有返回承诺,await 响应没有意义 - 所以,我将它包装在一个新的承诺中 - 如果我有时间,可能有更简单的方法来做到这一点不用担心 async/await,因为实际上代码中根本没有任何承诺,除了没有引入使用 async/await 的承诺

不过,我相信这样的代码更容易阅读,所以,让我们继续使用 async/await 和 promise 主题

其次,使用for ... of 循环使代码非常简单,特别是因为您希望任何错误都停止对Filters.getFiltersByParentId 的进一步调用

代码如下

module.exports.getFilters = (callback, limit) => {

    Filters.find({bind_to: null}, async (err, filters) => {
        if (err) {
            console.log(err);
            let obj = {status: false, error: err.errmsg};
            callback(obj);
        } else {
            const resObj = [];
            for (const ele of filters) {
                try {
                    let result = await new Promise((resolve, reject) => {
                        Filters.getFiltersByParentId(ele._id, (err, cfil) => {
                            if (err) {
                                let obj = {status: false, message: err.errmsg};
                                reject(obj);
                            } else {
                                console.log(ele, "Obj");
                                ele.children = cfil;
                                resolve(ele);
                            }
                        });
                    });
                    resObj.push(result);
                } catch(e) {
                    return callback(obj);
                }
            }
            let obj = {status: true, data: resObj, message: "Filters found"};
            callback(obj);
        }
    });
};

编辑:我有时间:p

这是没有 async/await 的代码,因为没有承诺

module.exports.getFilters = (callback, limit) => {
    Filters.find({bind_to: null}, (err, filters) => {
        if (err) {
            console.log(err);
            let obj = {status: false, error: err.errmsg};
            callback(obj);
        } else {
            const resObj = [];
            const getFilters = (index) => {
                if (index < filters.length) {
                    const ele = filters[index];
                    Filters.getFiltersByParentId(ele._id, (err, cfil) => {
                        if (err) {
                            let obj = {status: false, message: err.errmsg};
                            callback(obj);
                            return;
                        } else {
                            console.log(ele, "Obj");
                            ele.children = cfil;
                            getFilters(index + 1);
                        }
                    });
                } else {
                    let obj = {status: true, data: resObj, message: "Filters found"};
                    callback(obj);
                }
            };
            getFilters(0);
        }
    });
};

【讨论】:

  • 这个我刚才试过了,这个不会响应!这里的结果将永远是undefined
  • 我认为 Filters.getFiltersByParentId 因您的原始代码而返回了一个承诺 - 我现在看到如果 Filters.getFiltersByParentId 不返回承诺,您的原始代码存在根本缺陷 - 我'如果现在我看到它需要一个回调参数这一事实,我实际上会感到惊讶!
  • 我已经重写了答案
  • @rakcode - 我添加了一个更好的答案,因为您的原始代码中没有 Promise,只有您认为存在的 Promise!
  • 在回复第一个之前,您是否阅读了我制作的所有 cmets? @rakcode
【解决方案2】:

在对我的问题进行了一些更改并使用@Jaromanda X's@CertainPerformance 指南后,我想出了一个解决问题的方法。这与我想要的不完全一样,但输出完全相同。

我将 gFilters.getFiltersByParentId 方法更改为返回 Promise

module.exports.getFiltersByParentId = (pid, callback) => {
    return new Promise(function(resolve, reject) {
        Filters.find({bind_to: pid._id}, function (err, results) {
            if (err) {
                reject(err);
            } else {
                let obj = {parent: pid, children: results};
                resolve(obj);
            }
        })
    });
}

然后将forEach更改为array.map并在Promise.all函数中使用promise数组

module.exports.getFilters = (callback, limit) => {
    Filters.find({bind_to: null}, (err, filters) => {
        if (err) {
            console.log(err);
            let obj = {status: false, error: err.errmsg};
            callback(obj);
        } else {
            const resObj = [];
            var promises = [];

            filters.map(function (ele) {
                promises.push(Filters.getFiltersByParentId(ele, (err, cfil) => {
                    if (err) {
                        let obj = {status: false, message: err.errmsg};
                        callback(obj);
                    } else {
                        console.log(ele, "Obj");
                        ele.children = cfil;
                        resObj.push(ele);
                    }
                }));
            });

            Promise.all(promises).then(res => {
                let obj = {status: true, data: res, message: "Filters found"};
                callback(obj);
            });

        }
    });
}

最后,我的输出看起来像这样

[
 {
  parent: {
   --- parent object ---
  },
  children: [
    {
     --- child object 1 ---
    },
    {
     --- child object 2 ---
    },
    {
     --- child object n ---
    }
  ]
 }
]

【讨论】:

    猜你喜欢
    • 2023-03-16
    • 2017-07-12
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-06
    • 2016-05-08
    • 1970-01-01
    相关资源
    最近更新 更多