我有一个用户数据库,我想为它们设置经度和纬度。但是,经过6次以上的调用后,我得到错误400,请求错误。我认为这是因为我对谷歌地图API做了太多调用,所以决定创建一个setTimeout函数,所以我会每隔1秒获得一次坐标。
然而,我发现我的forEach行为很奇怪。这是代码,然后我将解释什么是错误的。 (我认为代码的一部分是相关的)
let noOfSuccess = 0;
let noCoords = 0;
let forEachPromise = new Promise((resolve, reject) => {
arr.forEach(function (user) {
console.log('street', user.street)
let coordPromise = new Promise((resolve, reject) => {
let street = user.street;
let city = user.city;
let address = street.concat(', ').concat(city);
const url = `https://maps.googleapis.com/maps/api/geocode/json?address=${address}&key=APIKEY`;
setTimeout(function () {
let coords = axios.get(url).then((response) => {
return response;
})
resolve(coords);
}, 1000);
})
coordPromise.then(response => {
if (response.data.results[0].types == "street_address") {
console.log('adres', response.data.results[0].formatted_address)
arrSucc.push(response.data.results[0].formatted_address);
noOfSuccess++;
} else {
arrFail.push(response.data.results[0].formatted_address);
noCoords++;
}
console.log('coordResp', 'succ', noOfSuccess, 'fail', noCoords)
})
});
我希望如何工作: 我从数据库中获取用户,我在console.log中测试街道名称。然后我创造了一个承诺。在承诺中我等待1秒钟来调用谷歌API。在得到答复之后,我解决了这个承诺。 然后我接受响应,做一些检查和console.log发生了什么,无论是成功还是失败。然后我去找下一个用户。 首选输出: 用户街 - >谷歌API调用 - >记录成功或失败 对所有用户重复。
然而,正在发生的事情是: 它记录用户的所有街道,然后转到承诺,1秒后它立即调用API而不等待每秒1秒,然后在成功或失败时记录每个用户。看起来如何:
now listening for requests
street Kwiatowa 40
street Kwiatowa 40
street Kwiatowa 43
street Kwiatowa 36
street Kwiatowa 27
street Kwiatowa 42
street Kwiatowa 29
street Kwiatowa 45
(node:5800) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Request failed with status code 400
(node:5800) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
adres Kwiatowa 36, 02-579 Warszawa, Poland
coordResp succ 1 fail 0
adres Kwiatowa 43, 02-579 Warszawa, Poland
coordResp succ 2 fail 0
adres Kwiatowa 40, 02-579 Warszawa, Poland
coordResp succ 3 fail 0
adres Kwiatowa 27, 02-579 Warszawa, Poland
coordResp succ 4 fail 0
adres Kwiatowa 29, Radom, Poland
coordResp succ 5 fail 0
adres Kwiatowa 42, 02-579 Warszawa, Poland
coordResp succ 6 fail 0
adres Kwiatowa 40, 02-579 Warszawa, Poland
coordResp succ 7 fail 0
我做错了什么?我理解promises或forEach循环是否存在问题?
答案 0 :(得分:0)
我会避免将Array.prototype.forEach
与Promises一起使用,因为在implementation中,它不会等待回调函数完成,然后再迭代数组中的下一项(这解释了行为)你提到的)。
对于这种情况,我的建议是对async/await使用for循环:
async function getDataForUsers () {
for (let user of arr) {
console.log('street', user.street)
const response = await myPromiseFunction(user) // inside this function you can make your api calls with axios and setTimeout
// do some stuff with response
}
}
我还将看一看bluebird模块,该模块针对您的用例可能有一些有趣的方法用于不同的实现。
答案 1 :(得分:0)
有几种方法可以解决此问题。您可以创建基于Promise的解决方案,使用async / await或使用RxJS。
在这种情况下,我建议稍微重构一下代码,看看Promise的功能,还可以检查bluebird库以在Promises上具有更多功能。
核心问题是异步性:您正在安全地调用所有Promises,即使它们试图等待一秒钟,它们也会一次进入事件队列(比如说)。您要做的是在处理每个用户后等待一秒钟。
使用单独的承诺,您可以尝试递归策略
// The only responsible to know **what** to do with each user
function processUser(user) {
console.log(user.street, user.city);
// then method returns a new Promise that is resolved when the passed function is called, also it resolves to the value returned
return fetchUserLocation.then(function(location) {
console.log(location);
// do someStuff after you have fetched the data
});
}
// The only responsible to know **when** to do stuff with an user
function controlFetchAllUsers(users, index, delayMills) {
var currentUser = users[index];
var hasNextUser = index < users.length - 1;
processUser(currentUser).then(function() {
// Once we processed the user, we can now wait one second
// If there is another user
if (hasNextUser) {
setTimeout(
function() {
controlFetchAllUsers(users, index + 1, delayMills)
},
delayMills
);
}
});
}
controlFetchAllUsers(users, 0, 1000);
出于某些考虑,您可能想知道所有过程何时完成,为此,您可以使用包装整个过程的Promise,并也许使用延迟模式。
另一方面,对于此类问题,我真诚地推荐RxJS(但是,任何反应式编程库可能都非常有用)
祝你好运!