Firebase Функции Как отправить после того, как цикл завершает итерацию - PullRequest
1 голос
/ 02 ноября 2019

Здравствуйте, у меня есть некоторые проблемы с функциями Firebase, но проблема, скорее всего, больше связана с JavaScript. Вы видите, мне нужна функция для возврата клиенту массива объектов, которые находятся в пределах диапазона местоположения клиента. Проблема заключается в том, что этой функции необходимо выполнить итерации и протестировать документы в коллекции, чтобы увидеть, находятся ли они в пределах досягаемости. Если это так, поместите объект в массив. Как только он завершит итерацию каждой вещи, верните массив обратно клиенту.

Основная проблема заключается в том, что JavaScript не работает синхронно, когда дело доходит до циклов. Поэтому моей следующей идеей было попытаться заставить ожидание после завершения итерации, используя обещания. Ниже приведен код, который у меня есть:

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. Я посмотрел, как 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();

});

Проблема теперь в том, что ЕЩЕ все равно возвращает пустую информацию клиенту. Не только это, но и эта пустая информация отправляется до завершения цикла. Итак, на моей консоли я вижу, что массив, который должна возвращать функция, заполнен правильной информацией для отправки обратно. Проблема, однако, в том, что функция по-прежнему ничего не отправляет, я подозреваю, потому что она все еще отправляет до завершения цикла. Может кто-нибудь сказать мне, почему это так?

1 Ответ

0 голосов
/ 03 ноября 2019

Хорошо, ребята, так что я понял, в чем проблема! Каждый раз, когда вы хотите, чтобы функция Firebase возвращалась после асинхронной операции, вы должны вернуть обещание. Таким образом, в приведенном выше коде каждый раз, когда выполнялась асинхронная операция, я должен был поместить return перед ней. Не только это, но я также должен был вкладывать все одно за другим в функции .then (). Не самое легкое занятие, так как когда вы имеете дело с большими объемами кода, требующими значительного количества асинхронных операций, очень легко потеряться в вашем коде. Но вот как работает Firebase (или облачные функции Google, один из двух). Возможно, в будущем они облегчат выполнение асинхронных функций, но это было главной проблемой. В моем коде ниже вы можете видеть, что каждая асинхронная функция возвращается, и все основные части кода расположены в .then () предыдущей функции.

Я отмечаю, что это можно упроститьнемного, так как в то время как Promises являются важной частью, они не были главной проблемой с кодом. Проблема заключалась в том, как 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();

});

Как видите, постоянное птенцование может быть довольно подавляющим, но в данный момент это неизбежное зло, и, возможно, это изменится вбудущее будет рассматриваться более элегантно.

...