#javascript #node.js
#javascript #node.js
Вопрос:
У меня дома небольшая настройка IoT, и мне нужно повторно запрашивать небольшое количество RESTful API для получения данных с нескольких датчиков. Каждый URL-адрес должен запрашиваться с использованием пользовательской задержки между различными попытками.
Каков наилучший способ реализовать это в Node.JS ? Конечная цель — проанализировать возвращенный JSON и сохранить его в Realm DB.
Я пытаюсь реализовать это в promises и изо всех сил пытаюсь придумать две вещи: 1) куда я должен поместить then() для обработки ответа сервера (имитируемого как setTimeout), 2) как перенести URL-адрес после тайм-аута.
const conf = [
{
url: "http://google.com",
timeout: 2400, // ms between queries
},
{
url: "http://duckduckgo.com",
timeout: 2000,
}
];
let activePromises = [];
conf.forEach( function(el) {
activePromises.push( new Promise(
function webHandler(resolve, reject) {
console.log(`Starting request to ${ el.url }...`);
setTimeout( () => {
console.log(`Resolving ${ el.url } after ${ el.timeout }ms`);
resolve('body'); // real-world contents will be a JSON document
// how to repeat the same API query after el.timeout ms???
}, 30);
}
));
});
Ответ №1:
Используйте Promise.all()
вместе с map()
const conf = [
{
url: "http://google.com",
timeout: 2400, // ms between queries
},
{
url: "http://duckduckgo.com",
timeout: 2000,
}
];
Promise.all(conf.map(async item=> await doTask(item.url,item.timeout)))
const doTask=(url,timeout)=>{
return new Promise((resolve, reject)=> {
.....
}
}
Комментарии:
1. он не показывает, как на самом деле обрабатывать разное время ожидания для каждого обещания, имеет бесполезный конструктор new Promise, которого там не должно быть, и
Promise.all
используется без всякой причины
Ответ №2:
Если вам нужно повторять действие каждые n секунд, вы ищете setInterval
const conf = [
{
url: "http://google.com",
timeout: 2400, // ms between queries
},
{
url: "http://duckduckgo.com",
timeout: 2000,
}
];
const fetchResult = url => api(url) //pseudo code here
let results = Array(conf.length).fill(Promise.resolve(null))
conf.foreach(({url, timeout}, i) => {
setInterval(async() => {
const res = await fetchResult(url)
results[i] = res;
}, timeout)
})
в массиве результатов у вас есть n обещаний.
Тогда вы можете использовать обещание.все, если хотите, или рассматривайте их отдельно
Комментарии:
1. как можно использовать постоянно изменяющийся массив обещаний, на которые вы ссылаетесь? когда вы хотите вызвать Promise.all?
Ответ №3:
Допустим (для простоты) loadData
, это функция, которая будет извлекать данные из URL-адреса (который вы в настоящее время смоделировали с использованием таймаута).
Если вы хотите начать отправлять запросы через регулярные промежутки времени, вы можете просто использовать setInterval:
conf.forEach(el => {
setInterval(() => {
loadData(el.url)
.then((body) => { /** Save to DB here */ });
.catch(() => { /** Handle error */ });
}, el.timeout);
});
Но если вы хотите дождаться разрешения loadData, а затем дождаться истечения времени ожидания, вы можете сделать что-то вроде:
function process() {
loadData(el.url).then((body) => {
/** Save to DB here */
setTimeout(process, el.timeout);
}).catch(() => { /** Handle error */ });
}
который вызовет ту же функцию по истечении указанного времени ожидания.
Комментарии:
1. Ваш первый пример верен. Во втором примере должно учитываться произвольное количество
then
секунд,setTimeout
так как сохранение в БД является асинхронной задачей. Самое главное,setTimeout
это не рекурсия и не увеличит стек вызовов2. @Max спасибо за предложение, я не знал о концепции асинхронных стеков, и когда я выполнил это и просмотрел в инструментах разработки, подумал, что это фактический стек вызовов. Я отредактировал свой ответ.