【问题标题】:Firebase Functions How To Send After Loop Finishes IteratingFirebase 函数如何在循环完成迭代后发送
【发布时间】:2020-02-28 14:58:20
【问题描述】:

您好,我在使用 Firebase 函数时遇到了一些问题,但问题很可能更多地出现在 JavaScript 方面。你看我需要一个函数来向客户端返回一个在客户端位置范围内的对象数组。问题是这个函数需要迭代和测试集合中的文档以查看它们是否在范围内。如果是,则将对象推到阵列上。一旦它完成了每件事的迭代,将数组返回给客户端。

主要问题是 JavaScript 在循环方面不是同步的。所以我的下一个想法是尝试在迭代完成后使用 Promise 强制等待。下面是我目前拥有的代码:

export const getNearbyBison = functions.https.onCall((data, context) => {
    let distance = data.distance;
    let thisUid = data.uid;
    let Lat1 = data.x;
    let Lon1 = data.y;
    let returnUIDs: any[] = [];
    console.log(distance, thisUid, Lat1, Lon1);
    var needDocs = admin.firestore().collection('users').doc('user').collection('user').where('locationEnabled', '==', true).get();

    return needDocs.then(documents => {
        console.log(documents);
        let theseDocs = documents.docs;
        let promiseCheck: any[] = [];
        theseDocs.forEach(doc => {
            let pUid = doc.data().uid;
            console.log('Uid of this user' + pUid);
            admin.firestore().collection('location').doc(pUid).get().then(async location => {
                if (location.exists) {
                    console.log('Location of this user' + location);
                    let locID = location.id;
                    let thisData = location.data();
                    let Lat2 = thisData!.x;
                    let Lon2 = thisData!.y;
                    console.log(locID, Lat2, Lon2);
                    //**********Location equation to solve distance*********
                    var R = 6371000;
                    var phi1 = Lat1 * Math.PI / 180;
                    var phi2 = Lat2 * Math.PI / 180;
                    var deltaPhi = Lat2 - Lat1;
                    var deltaLambda = Lon2 - Lon1;
                    var dLat = deltaPhi * Math.PI / 180;
                    var dLon = deltaLambda * Math.PI / 180;

                    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                        Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLon / 2) *
                        Math.sin(dLon / 2);

                    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                    var d = R * c;
                    //*******Convert to miles**************
                    var miles = d / 1609.34;
                    console.log('Miles of user: ' + miles);

                    let pusher = new Promise((resolve, reject) => {
                        if (miles <= distance) {
                            if (thisUid != locID) {
                                //Remember this setup for when you have to use the info for this page...
                                returnUIDs.push({
                                    farAway: miles,
                                    userInfo: doc
                                });
                                resolve();
                            } else { resolve() }
                        } else { resolve() }
                    });

                    promiseCheck.push(
                      pusher  
                    )
                }
            }).catch();
        })
        Promise.all(promiseCheck).then(whatever => {
            console.log(returnUIDs);
            return ({
                nearbyArray: returnUIDs
            });
        }).catch();
    }).catch();

});

问题在于,在 forEach 循环完成迭代之前,该函数仍在将数组发送到客户端。有没有办法强迫它等待?我究竟做错了什么?我在这个论坛上查看了其他类似的问题,这就是我得出的结论,即我需要以某种方式使用 Promises 以使程序等待循环完成。提前谢谢你。

编辑:: 好的,所以我取得了相当大的进步。首先,我将 forEach() 循环中的所有内容放入一个可返回的 Promise 中,然后将每个内容推送到 Promise 数组中。我查看了 Javascript 如何处理异步操作,这就是为什么它在循环完成之前将一个空数组记录到我的控制台的原因。我检查了我的控制台,现在它确实在 forEach 循环完成后记录了返回的数组。这是编辑后的代码:

export const getNearbyBison = functions.https.onCall((data, context) => {
let distance = data.distance;
let thisUid = data.uid;
let Lat1 = data.x;
let Lon1 = data.y;
let returnUIDs: any[] = [];
console.log(distance, thisUid, Lat1, Lon1);
var needDocs = admin.firestore().collection('users').doc('user').collection('user').where('locationEnabled', '==', true).get();

needDocs.then(documents => {
    console.log(documents);
    let theseDocs = documents.docs;
    let promiseCheck: any[] = [];
    theseDocs.forEach(doc => {
        let pusher = new Promise((resolve, reject) => {
            let pUid = doc.data().uid;
            console.log('Uid of this user' + pUid);
            admin.firestore().collection('location').doc(pUid).get().then(location => {
                if (location.exists) {
                    console.log('Location of this user' + location);
                    let locID = location.id;
                    let thisData = location.data();
                    let Lat2 = thisData!.x;
                    let Lon2 = thisData!.y;
                    console.log(locID, Lat2, Lon2);
                    //**********Location equation to solve distance*********
                    var R = 6371000;
                    var phi1 = Lat1 * Math.PI / 180;
                    var phi2 = Lat2 * Math.PI / 180;
                    var deltaPhi = Lat2 - Lat1;
                    var deltaLambda = Lon2 - Lon1;
                    var dLat = deltaPhi * Math.PI / 180;
                    var dLon = deltaLambda * Math.PI / 180;

                    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                        Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLon / 2) *
                        Math.sin(dLon / 2);

                    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                    var d = R * c;
                    //*******Convert to miles**************
                    var miles = d / 1609.34;
                    console.log('Miles of user: ' + miles);

                    if (miles <= distance) {
                        if (thisUid != locID) {
                            //Remember this setup for when you have to use the info for this page...
                            returnUIDs.push({
                                farAway: miles,
                                userInfo: doc
                            });
                            resolve();
                        } else { resolve() }
                    } else { resolve() }

                }
            }).catch();
        });
        promiseCheck.push(pusher);
    });

    Promise.all(promiseCheck).then(whatever => {
        console.log(returnUIDs);
        return ({
            nearbyArray: returnUIDs
        });
    }).catch();
}).catch();

});

现在的问题是它仍然向客户端返回空信息。不仅如此,而且在循环完成之前发送了这些空信息。所以在我的控制台上,我可以看到函数应该返回的数组填充了正确的信息以发回。然而,问题是该函数仍然没有发送任何内容,我怀疑是因为它仍在循环完成之前发送。谁能告诉我为什么会这样?

【问题讨论】:

  • 简单地调用thencatch 并不会真正导致任何等待。您的代码需要返回一个单一的承诺,该承诺会在 all 函数中的异步工作完成后与发送给客户端的数据一起解析。这意味着每个异步调用返回的所有其他承诺都需要考虑到最终的承诺中。
  • 嗯,你能帮我澄清一下吗?因为我认为这就是我向数组添加承诺然后等待该承诺数组解决的方法。但显然我不是lol。
  • 正如我所说,调用then 不会“等待”任何事情完成。它是异步的,只是立即返回另一个 Promise,该 Promise 使用您传递的回调返回的值解析。 Promise.all() 也是异步的并返回另一个承诺。也许你想用它做点什么,因为你目前正在忽略它?
  • 哦,好的,谢谢!那么在 promise 数组解析后我将如何发送数组呢?
  • 正如我所说:“在函数中的所有异步工作完成后,您的代码需要返回一个与发送给客户端的数据一起解析的承诺。”这里有很多代码,我无法真正为您调试所有代码。我可以告诉你,我可以看到你在这里忽略了承诺,这导致你的功能中断。或许先尝试一些简单的事情来进行实验和学习,然后再逐步找到最终解决方案。

标签: javascript firebase foreach promise google-cloud-functions


【解决方案1】:

好的,伙计们,我发现了问题所在!每当您希望 Firebase 函数在异步操作后返回时,您都必须返回 Promise。因此,在上面的代码中,每次执行异步操作时,我都必须将 return 放在它前面。不仅如此,我还必须将所有内容一个接一个地嵌套在 .then() 函数中。当您处理需要大量异步操作的大量代码时,这不是最简单的壮举,很容易迷失在代码中。但是,这就是 Firebase 的工作方式(或谷歌云功能,两者之一)。也许他们将来会更容易执行异步功能,但这是主要问题。在我下面的代码中,你可以看到每个异步函数都被返回,并且代码的所有主要部分都嵌套在前一个函数的 .then() 中。

我确实注意到它可以简化一点,因为虽然 Promise 是一个重要部分,但它们并不是代码的主要问题。问题在于 Firebase(或 Google)如何处理异步代码。

export const getNearbyBison = functions.https.onCall((data, context) => {
    let counter = 0;
    let distance = data.distance;
    let thisUid = data.uid;
    let Lat1 = data.x;
    let Lon1 = data.y;
    let returnUIDs: any[] = [];
    let promiseCheck: any[] = [];
    console.log(distance, thisUid, Lat1, Lon1);
    var needDocs = admin.firestore().collection('users').doc('user').collection('user').where('locationEnabled', '==', true).get();

    return needDocs.then(documents => {
        return new Promise((r, j) => {
            console.log(documents);
            let theseDocs = documents.docs;

            theseDocs.forEach(doc => {
                let pusher = new Promise((resolve, reject) => {
                    let pUid = doc.data().uid;
                    console.log('Uid of this user' + pUid);
                    admin.firestore().collection('location').doc(pUid).get().then(location => {
                        if (location.exists) {
                            console.log('Location of this user' + location);
                            let locID = location.id;
                            let thisData = location.data();
                            let Lat2 = thisData!.x;
                            let Lon2 = thisData!.y;
                            console.log(locID, Lat2, Lon2);
                            //**********Location equation to solve distance*********
                            var R = 6371000;
                            var phi1 = Lat1 * Math.PI / 180;
                            var phi2 = Lat2 * Math.PI / 180;
                            var deltaPhi = Lat2 - Lat1;
                            var deltaLambda = Lon2 - Lon1;
                            var dLat = deltaPhi * Math.PI / 180;
                            var dLon = deltaLambda * Math.PI / 180;

                            var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                                Math.cos(phi1) * Math.cos(phi2) * Math.sin(dLon / 2) *
                                Math.sin(dLon / 2);

                            var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                            var d = R * c;
                            //*******Convert to miles**************
                            var miles = d / 1609.34;
                            console.log('Miles of user: ' + miles);

                            if (miles <= distance) {
                                if (thisUid != locID) {
                                    //Remember this setup for when you have to use the info for this page...
                                    returnUIDs.push({
                                        farAway: miles,
                                        userInfo: doc
                                    });
                                    counter++;
                                    resolve();
                                } else {
                                    counter++;
                                    resolve();
                                }
                            } else {
                                counter++;
                                resolve();
                            }
                            if (counter >= documents.size) {
                                r();
                            }

                        }
                    }).catch();
                });
                promiseCheck.push(pusher);
            });

        }).then(() => {
            return Promise.all(promiseCheck).then(whatever => {
                console.log(returnUIDs);
                return { nearbyArray: returnUIDs };
            }).catch();
        }).catch();
    }).catch();

});

正如您所见,不断的筑巢可能会让人不知所措,但目前它是一种必要的邪恶,也许将来会改变,以便更优雅地处理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 2016-11-03
    • 2017-11-29
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    相关资源
    最近更新 更多