【发布时间】: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();
});
现在的问题是它仍然向客户端返回空信息。不仅如此,而且在循环完成之前发送了这些空信息。所以在我的控制台上,我可以看到函数应该返回的数组填充了正确的信息以发回。然而,问题是该函数仍然没有发送任何内容,我怀疑是因为它仍在循环完成之前发送。谁能告诉我为什么会这样?
【问题讨论】:
-
简单地调用
then或catch并不会真正导致任何等待。您的代码需要返回一个单一的承诺,该承诺会在 all 函数中的异步工作完成后与发送给客户端的数据一起解析。这意味着每个异步调用返回的所有其他承诺都需要考虑到最终的承诺中。 -
嗯,你能帮我澄清一下吗?因为我认为这就是我向数组添加承诺然后等待该承诺数组解决的方法。但显然我不是lol。
-
正如我所说,调用
then不会“等待”任何事情完成。它是异步的,只是立即返回另一个 Promise,该 Promise 使用您传递的回调返回的值解析。Promise.all()也是异步的并返回另一个承诺。也许你想用它做点什么,因为你目前正在忽略它? -
哦,好的,谢谢!那么在 promise 数组解析后我将如何发送数组呢?
-
正如我所说:“在函数中的所有异步工作完成后,您的代码需要返回一个与发送给客户端的数据一起解析的承诺。”这里有很多代码,我无法真正为您调试所有代码。我可以告诉你,我可以看到你在这里忽略了承诺,这导致你的功能中断。或许先尝试一些简单的事情来进行实验和学习,然后再逐步找到最终解决方案。
标签: javascript firebase foreach promise google-cloud-functions