Как я могу добавить setTimeout к моему вызову функции async await?
у меня есть
request = await getProduct(productids[i]);
где
const getProduct = async productid => {
return requestPromise(url + productid);
};
я пытался
request = await setTimeout((getProduct(productids[i])), 5000);
и получил ошибку TypeError: "callback" argument must be a function, которая имеет смысл. Запрос находится внутри цикла, из-за чего я достиг предела скорости при вызове API.
exports.getProducts = async (req, res) => {
let request;
for (let i = 0; i <= productids.length - 1; i++) {
request = await getProduct(productids[i]);
//I want to wait 5 seconds before making another call in this loop!
}
};Вы имеете в виду, что запрос выполняется через 1 секунду или вы запускаете его немедленно, а если он не завершается в течение 1 секунды, вы его закрываете?
К вашему сведению, библиотека запроса-обещания уже поддерживает параметр тайм-аута, поэтому, если вы просто хотите тайм-аут ожидания ответа, вы можете использовать параметр, уже встроенный в библиотеку просьба-обещание.
@ jfriend00 спасибо за комментарии. Я действительно хочу поставить функцию ожидания, чтобы после одного запроса она ждала, прежде чем делать другой запрос. Раньше я не видел опции тайм-аута в библиотеке, спасибо за ссылку.
Этот параметр тайм-аута не является параметром ожидания для следующего запроса. Это совершенно разные вещи.
@ jfriend00 Я понимаю это. Я добавил некоторые детали к вопросу, чтобы, надеюсь, прояснить, что мне не нужен тайм-аут для вызова API, а перерыв между вызовами





На самом деле у меня есть довольно стандартный фрагмент кода, который я использую для этого:
function PromiseTimeout(delayms) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delayms);
});
}
Применение:
await PromiseTimeout(1000);
Если вы используете обещания Bluebird, они встроены как Promise.timeout.
Еще о вашей проблеме: вы проверили документацию по API? Некоторые API-интерфейсы сообщают вам, сколько вам нужно ждать до следующего запроса. Или разрешите загрузку больших объемов данных.
В Bluebird это Promise.delay вместо Promise.timeout.
Вы можете использовать небольшую простую функцию, которая возвращает обещание, которое разрешается после задержки:
function delay(t, val) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(val);
}, t);
});
}
И затем await внутри вашего цикла:
exports.getProducts = async (req, res) => {
let request;
for (let id of productids) {
request = await getProduct(id);
await delay(5000);
}
};
Примечание: я также переключил ваш цикл for на использование for/of, который не требуется, но он немного чище, чем у вас.
Спасибо! Это идеально, спасибо за включение примера использования. Также оцените изменение цикла for, не знал, что вы можете это сделать, поэтому я узнал дважды из этого ответа.
У меня есть один вопрос: знаете ли вы, ожидает ли асинхронный метод с использованием await завершения одного вызова API перед выполнением следующего? Если это так, то эта задержка необходима, если нет, мне может потребоваться найти способ делать вызовы серии, чтобы избежать жесткого кодирования в такой задержке.
@jenryb - Если getProduct(id) возвращает обещание, которое разрешается только после выполнения его асинхронной операции, тогда await getProduct(id) действительно будет ждать его завершения, а цикл for действительно будет ждать каждого вызова getProduct(id). Но (и это важно) ваша экспортированная функция getProducts() не ждет возврата. Он немедленно возвращается и возвращает обещание, которое разрешается, когда цикл for и все операции await выполнены.
Начиная с узла v15 вы можете использовать API таймеров обещаний:
const timersPromises = require('timers/promises');
async function test() {
await timersPromises.setTimeout(1000);
}
test();
Обратите внимание, что эта функция является экспериментальной и может измениться в будущих версиях.
Начиная с Node 15 и выше, есть новый API таймеров обещаний, который позволяет избежать создания упаковки:
import {
setTimeout,
setImmediate,
setInterval,
} from 'timers/promises';
console.info('before')
await setTimeout(1000)
console.info('after 1 sec')
Итак, ваши проблемы вы можете написать с помощью асинхронного итератора:
import {
setTimeout
} from 'timers/promises'
async function getProducts (req, res) {
const productids = [1, 2, 3]
for await (const product of processData(productids)) {
console.info(product)
}
}
async function * processData (productids) {
while (productids.length > 0) {
const id = productids.pop()
const product = { id }
yield product
await setTimeout(5000)
}
}
getProducts()
Вот очень похожий вопрос: Как сделать таймаут кошмарного запроса.