我正在尝试调用 Yelp API 中的单独端点来提取业务描述信息和营业时间。我在 Express 中定义了初始 POST 路由,并使用 ejs,在调用第一个端点时能够显示数据。我需要将业务 ID 从第一个端点中创建的数组传递到对第二个端点的调用,但是我收到 TooManyRequests
响应,因此必须使用 setTimeout
;这就是我遇到问题的地方
虽然setTimeout
允许我console.log第二次调用返回的结果,但它实际上不允许我返回任何数据以呈现给页面。没有抛出任何错误,也没有返回未定义
,这进一步增加了我的困惑。
我尝试将 setTimeout
包装到 for 循环内部和外部的 IIFE 中,这产生了与以前相同的结果。我曾短暂考虑过实现 Promises,但正在考虑是否可以仅使用 setTimeout 来实现。不确定我在这里是否使用了正确的方法,但任何帮助或澄清将不胜感激。
app.js
express = require("express");
var app = express();
var path = require("path");
var yelp = require("yelp-fusion");
var request = require("request");
var bodyParser = require("body-parser");
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended: true}));
app.set("view engine", "ejs");
let client = yelp.client("API_KEY_HID");
app.get("/", function(req,res){
res.render("landing");
});
app.post("/", function(req, res){
client.search({
term: 'coffee',
location: 'Oakland',
limit: 10
}).then(response => {
var businesses = response.jsonBody.businesses;
var idArray = [];
var openHours;
var id = businesses.forEach(el => {
idArray.push(el.id);
});
for(var x = 0; x < businesses.length; x++){
var delay = 1 * 1000;
setTimeout(function(x){
client.business(idArray[x]).then(response => {
var hours = response.jsonBody.hours.map(el => {
return el.open;
});
openHours = hours.map(lo => {
return lo[x].start;
});
return openHours;
});
},delay*x,x)
}
res.render('search', {
hour: openHours
});
}).catch(e => {
console.log(e);
});
});
app.listen(3000);
最佳答案
您的代码存在很多问题。对于初学者来说,setTimeout()
是异步且非阻塞的。这意味着您的 for()
循环运行以完成设置一堆计时器,您可以在任何之前调用 res.render('search', {hour: openHours})
setTimeout()
回调已被调用,因此 openHours
仍然为空。
然后,为了解决您遇到的 TooManyRequests
错误,您必须重新设计发出请求的方式以及在请求全部完成时进行监控的方式。为了以最佳方式做到这一点,您必须知道来自您请求数据的主机的实际请求限制是多少。您需要知道是否限制同时进行的并行请求数、限制在特定时间段内的一定数量的请求(例如 1 个请求/秒)或者是否有其他规则。
在不知道实际限制的情况下,您可以设计一个串行请求系统(一次一个请求,请求之间的延迟可调)。您很可能可以对其进行调整,以适应主机强制执行的任何限制(除非您发出了太多的总请求)。如果您了解实际规则,您就可以设计更有效的代码。但是,在不了解规则的情况下,这里有一个可调整的序列化方法。
此外,代码中还有许多其他不清楚的部分,因为不清楚您在所有 setTimeout()
回调中尝试累积的具体内容。每个都将 openHours 设置为新值(覆盖以前的值),因此我无法准确说出您希望该数据是什么样子。您必须填写下面代码中的该部分。
这会序列化您的所有请求,一个接一个地在它们之间设置可设置的延迟。您希望可以对其进行调整,以减慢您的请求,使其达到主机可以接受的程度。
此代码使用 .reduce()
设计模式来序列化适用于任何环境的 Promise。还有许多其他设计模式可用于序列化基于 Promise 的操作。如果您有 ES7 环境,那么 for
循环中的 async/await 也可以是一种简单的方法。
var express = require("express");
var app = express();
var path = require("path");
var yelp = require("yelp-fusion");
var request = require("request");
var bodyParser = require("body-parser");
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({
extended: true
}));
app.set("view engine", "ejs");
// utility delay function that returns a promise
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(v), t);
});
}
let client = yelp.client("API_KEY_HID");
app.get("/", function(req, res) {
res.render("landing");
});
app.post("/", function(req, res) {
client.search({
term: 'coffee',
location: 'Oakland',
limit: 10
}).then(response => {
var businesses = response.jsonBody.businesses;
var idArray = businesses.map(el => el.id);
// set your delay value here between reqeusts (here it is set to 500ms)
const delayBetweenRequests = 500;
return idArray.reduce((id, p, i) => {
return p.then((array) => {
return client.business(id).then(response => {
// not sure what you want to get out of this response and add to the array of openHours
// that we're accumulating
array.push(something here to add to the results);
// return delay promise so the next request will wait
return delay(delayBetweenRequests, array);
});
});
}, Promise.resolve([])).then(openHours => {
res.render('search', {hour: openHours});
});
}).catch(e => {
console.log(e);
res.sendStatus(500);
});
});
app.listen(3000);
关于javascript - 调用 API 端点时的 setTimeout 不返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54056683/