简体   繁体   English

对API端点的调用中的setTimeout不返回值

[英]setTimeout in call to API endpoint not returning values

I'm attempting to make a call to a separate endpoint within the Yelp API to extract business description info and hours of operation. 我试图调用Yelp API中的另一个端点,以提取业务描述信息和营业时间。 I defined the initial POST route in Express and, using ejs, am able to display data when called for the first endpoint. 我在Express中定义了初始POST路由,并使用ejs在调用第一个端点时能够显示数据。 I need to pass business IDs from an array created in my first endpoint to a call to the second, however I am getting TooManyRequests responses and therefore had to make use of setTimeout ; 我需要将业务ID从在第一个端点中创建的数组传递给对第二个端点的调用,但是我收到TooManyRequests响应,因此必须使用setTimeout this is where I'm running into problems 这就是我遇到问题的地方

While setTimeout will allow me to console.log the returned results from the second call, it doesn't actually allow me to return any data to present to the page. 虽然setTimeout允许我console.log从第二次调用返回的结果,但实际上不允许我返回任何要显示在页面上的数据。 No errors are thrown and I am not being returned undefined , further adding to my confusion. 没有抛出任何错误,也没有返回undefined ,这进一步加剧了我的困惑。

I attempted to wrap setTimeout into an IIFE both inside and outside of my for-loop which yielded the same results as before. 我试图将setTimeout包装在for循环内部和外部的IIFE中,这产生了与以前相同的结果。 I briefly considered implementing Promises but was seeing if this could be accomplished solely using setTimeout . 我简短地考虑了实现Promises的问题,但是正在看是否可以仅使用setTimeout来实现。 Not sure if I'm using the proper approach here but any help or clarification would be greatly appreciated. 不知道我是否在这里使用正确的方法,但是任何帮助或澄清将不胜感激。

app.js 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); 

Here is the reason you are not seeing any data: 这是您看不到任何数据的原因:

the then function actually returns a Promise . then函数实际上返回一个Promise It resolves with the return value of the function that's passed into it , which in this case is openHours . 它使用传递给它的函数的返回值解析 ,在本例中为openHours

To be more expansive, client.business(id) is a Promise . 更广泛地说, client.business(id)是一个Promise client.business(id).then(resolvingFunction) is also a Promise . client.business(id).then(resolvingFunction) 也是 Promise As you know, when client.business(id) resolves, it executes the function passed into then . 如您所知,当client.business(id)解析时,它执行传递给then的函数。 What you didn't know is that client.business(id).then also needs to resolve . 您不知道的是client.business(id).then 还需要解决 The way you wrote it, it resolves with the value openHours . 您编写它的方式使用openHoursopenHours In order to execute, it needs it's own .then . 为了执行, 它需要自己的.then It would need to be client.business(id).then(...).then(openHours=>{do something with openHours}) . 它需要是client.business(id).then(...).then(openHours=>{do something with openHours}) You don't need two then s in this case, as long as instead of returning openHours in your then , you just do something with it. 你并不需要两个then只要不是在你返回openHours■在这种情况下, then ,你就用它做什么。 The smallest change possible to fix this issue would be moving res.render to the line that return openHours currently is. 解决此问题的最小更改是将res.render return openHours当前return openHours的行。 That would be kind of messy though. 但是,那会有点混乱。 Alternatively you could do this with multiple promises or using async / await . 或者,您可以使用多个promise或使用async / await来做到这一点。

Hope this helps. 希望这可以帮助。

There are lots of issues with your code. 您的代码有很多问题。 For starters, setTimeout() is asynchronous and non-blocking. 对于初学者来说, setTimeout()是异步的并且是非阻塞的。 That means your for() loop runs to completeion setting a bunch of timers, you call res.render('search', {hour: openHours}) before ANY of the setTimeout() callbacks have been called and thus openHours is still empty. 这意味着您的for()循环需要设置一组计时器才能完成,您需要在调用任何setTimeout()回调之前调用res.render('search', {hour: openHours}) ,因此openHours仍然为空。

Then, to solve the problem TooManyRequests error you're getting, you will have to redesign how you make requests and how you monitor when they are all done. 然后,要解决您遇到的TooManyRequests错误问题,您将不得不重新设计发出请求的方式以及如何监视请求完成的方式。 To do that optimally, you would have to know what your actual request limits are from the host you're requesting data from. 为了达到最佳效果,您将必须知道从其请求数据的主机的实际请求限制。 You need to know whether you are limited to a certain number of parallel requests in flight at the same time, limited to a certain number of requests within a certain time period (like 1 request/sec) or whether there is some other rule. 您需要知道您是同时限制在飞行中一定数量的并行请求,在特定时间段内限制在一定数量的请求(例如1个请求/秒)还是是否存在其他规则。

Without knowing how you're actually limited, you could design a serial request system (one request at a time with an adjustable delay between requests). 在不知道自己实际受到限制的情况下,您可以设计一个串行请求系统(一次请求一个,请求之间的延迟可调)。 Chances are you could tune that to work with whatever limits your host is enforcing (unless you're just making too many total requests). 您可能会调整为可以与主机强制执行的任何限制一起使用(除非您发出的请求总数过多)。 If you knew the actual rules, you could design more efficient code. 如果您知道实际的规则,则可以设计更有效的代码。 But, without knowing the rules, here's a tunable, serialized approach. 但是,在不知道规则的情况下,这是一种可调的,序列化的方法。

In addition, there were a number of other unclear portions of your code as it wasn't clear exactly what you were trying to accumulate in all your setTimeout() callbacks. 另外,代码中还有许多其他不清楚的部分,因为不清楚所有setTimeout()回调中您试图累积的内容。 Each one was setting openHours to a new value (overwriting the previous value) so I couldn't tell exactly what you want that data to look like. 每个人都将openHours设置为一个新值(覆盖先前的值),所以我无法确切地说出您想要该数据的外观。 You will have to fill in that portion in the code below. 您将必须在下面的代码中填写该部分。

This serializes all your requests, one after another with a settable delay between them. 这会序列化您的所有请求,一个接一个地延迟,一个接一个地延迟。 You can hopefully tune that to slow down your requests to something the host is OK with. 您可以对其进行调整,以将请求的速度减慢到主机可以接受的水平。

This code uses a .reduce() design pattern to serialize promises that works in any environment. 此代码使用.reduce()设计模式来序列化可在任何环境下工作的承诺。 There are many other design patterns for serializing promise-base operations too. 还有许多其他设计模式可用于序列化基于promise的操作。 If you have an ES7 environment, then async/await in a for loop can be a simple way to go too. 如果您有ES7环境,那么在for循环中进行异步/等待也是一种简单的方法。

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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM