setTimeout при вызове конечной точки API не возвращает значения - PullRequest
0 голосов
/ 06 января 2019

Я пытаюсь позвонить на отдельную конечную точку в API Yelp, чтобы извлечь информацию о бизнес-описании и часы работы. Я определил начальный маршрут POST в Express и, используя ejs, могу отображать данные при вызове для первой конечной точки. Мне нужно передать бизнес-идентификаторы из массива, созданного в моей первой конечной точке, на вызов второй, однако я получаю TooManyRequests ответов и поэтому должен был использовать setTimeout; это где я сталкиваюсь с проблемами

Хотя setTimeout позволяет мне console.log возвращать результаты второго вызова, на самом деле он не позволяет мне возвращать какие-либо данные для представления на странице. Нет ошибок, и я не возвращаюсь undefined, что еще больше усугубляет мою путаницу.

Я попытался обернуть setTimeout во IIFE как внутри, так и снаружи моего цикла for, что дало те же результаты, что и раньше. Я кратко подумал о реализации Обещаний, но смотрел, можно ли это сделать исключительно с помощью 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); 

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Есть много проблем с вашим кодом. Для начала setTimeout() является асинхронным и неблокирующим. Это означает, что ваш цикл for() выполняется до завершения установки таймеров, вы вызываете res.render('search', {hour: openHours}) до того, как будет вызван ЛЮБОЙ из setTimeout() обратных вызовов, и, таким образом, openHours все еще пуст.

Затем, чтобы решить возникшую ошибку TooManyRequests, вам нужно будет изменить способ подачи запросов и отслеживания их выполнения. Чтобы сделать это оптимально, вы должны знать, каковы ваши фактические пределы запросов от хоста, с которого вы запрашиваете данные. Вам необходимо знать, ограничено ли вами определенное количество параллельных запросов в полете одновременно, ограничено определенным количеством запросов в течение определенного периода времени (например, 1 запрос / сек) или существует какое-то другое правило.

Не зная, насколько вы на самом деле ограничены, вы можете разработать систему последовательных запросов (один запрос за раз с регулируемой задержкой между запросами). Скорее всего, вы можете настроить это для работы с любыми ограничениями, которые навязывает ваш хост (если вы просто не делаете слишком много общих запросов). Если бы вы знали настоящие правила, вы могли бы разработать более эффективный код. Но, не зная правил, вот настраиваемый, сериализованный подход.

Кроме того, в вашем коде присутствовал ряд других неясных частей, поскольку неясно, что именно вы пытаетесь накопить во всех ваших setTimeout() обратных вызовах. Каждый из них устанавливал openHours на новое значение (перезаписывая предыдущее значение), поэтому я не мог точно сказать, как вы хотите, чтобы эти данные выглядели. Вы должны будете заполнить эту часть в коде ниже.

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

Этот код использует шаблон проектирования .reduce() для сериализации обещаний, которые работают в любой среде. Существует также много других шаблонов проектирования для сериализации операций на основе обещаний. Если у вас есть среда 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);
0 голосов
/ 06 января 2019

Вот причина, по которой вы не видите никаких данных:

функция then фактически возвращает Promise. Преобразуется в возвращаемое значение функции, переданной в него , которая в данном случае равна openHours.

Чтобы быть более обширным, client.business(id) - это Promise. client.business(id).then(resolvingFunction) равно также a Promise. Как Вам известно, когда client.business(id) разрешается, он выполняет функцию, переданную в then. Что вы не знали, так это то, что client.business(id).then также необходимо разрешить . То, как вы это написали, разрешается со значением openHours. Для выполнения необходимо собственное .then. Это должно быть client.business(id).then(...).then(openHours=>{do something with openHours}). В этом случае вам не нужно два then с, если вместо того, чтобы возвращать openHours в вашем then, вы просто что-то с этим делаете. Наименьшее возможное изменение, чтобы исправить эту проблему, это переместить res.render на строку, которая в настоящее время return openHours. Это было бы немного грязно, хотя. В качестве альтернативы вы можете сделать это с несколькими обещаниями или используя async / await.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...