Сочетание асинхронной функции, await и setTimeout
Я пытаюсь использовать новые асинхронные функции и надеюсь, что решение моей проблемы поможет другим в будущем. Вот код, который работает:
async function asyncGenerator() {
// другой код
while (goOn) {
// другой код
var fileList = await listFiles(nextPageToken);
var parents = await requestParents(fileList);
// другой код
}
// другой код
}
function listFiles(token) {
return gapi.client.drive.files.list({
'maxResults': sizeResults,
'pageToken': token,
'q': query
});
}
Проблема заключается в том, что мой цикл while
работает слишком быстро, и скрипт отправляет слишком много запросов в секунду к API Google. Поэтому я хочу создать функцию задержки, которая будет откладывать запрос. Таким образом, я также смогу использовать эту функцию для задержки других запросов. Если есть другой способ затормозить запрос, пожалуйста, дайте знать.
Тем не менее, вот мой новый код, который не работает. Ответ запроса возвращается в анонимную асинхронную функцию внутри setTimeout
, но я просто не понимаю, как вернуть ответ в функцию sleep
или в первоначальную функцию asyncGenerator
.
async function asyncGenerator() {
// другой код
while (goOn) {
// другой код
var fileList = await sleep(listFiles, nextPageToken);
var parents = await requestParents(fileList);
// другой код
}
// другой код
}
function listFiles(token) {
return gapi.client.drive.files.list({
'maxResults': sizeResults,
'pageToken': token,
'q': query
});
}
async function sleep(fn, par) {
return await setTimeout(async function() {
await fn(par);
}, 3000, fn, par);
}
Я уже пробовал несколько вариантов: хранение ответа в глобальной переменной и возвращение его из функции sleep
, передача callback внутри анонимной функции и т. д.
5 ответ(ов)
Ваша функция sleep
не работает, потому что setTimeout
не (пока что?) возвращает промис, который можно было бы использовать с await
. Вам нужно вручную преобразовать его в промис:
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep(fn, ...args) {
await timeout(3000);
return fn(...args);
}
Кстати, для замедления вашего цикла, вероятно, не стоит использовать функцию sleep
, которая принимает колбек и откладывает его вызов таким образом. Я рекомендую использовать следующий подход:
while (goOn) {
// другой код
var [parents] = await Promise.all([
listFiles(nextPageToken).then(requestParents),
timeout(5000)
]);
// другой код
}
Этот способ гарантирует, что вычисление parents
займет как минимум 5 секунд.
Вы можете использовать следующий однострочный код, чтобы создать задержку в 1 секунду с помощью Promise
и setTimeout
:
await new Promise(resolve => setTimeout(resolve, 1000));
Этот код создаёт промис, который разрешается спустя 1000 миллисекунд (1 секунду).
С версии Node 7.6 вы можете использовать функцию promisify
из модуля util
в сочетании с функцией setTimeout()
. Это позволяет создавать асинхронную функцию "сна".
Node.js
const sleep = require('util').promisify(setTimeout)
Javascript
const sleep = m => new Promise(r => setTimeout(r, m))
Использование
(async () => {
console.time("Slept for")
await sleep(3000)
console.timeEnd("Slept for")
})()
В приведённом примере асинхронная функция sleep
вызывает setTimeout
, который откладывает выполнение на указанное количество миллисекунд (3000 мс или 3 секунды в данном случае). В результате выполнения кода вы увидите, что функция "заснула" на указанный интервал времени.
Если вы хотите использовать такой же синтаксис, как у setTimeout
, вы можете написать вспомогательную функцию, как в примере ниже:
const setAsyncTimeout = (cb, timeout = 0) => new Promise(resolve => {
setTimeout(() => {
cb();
resolve();
}, timeout);
});
Теперь вы можете вызвать её следующим образом:
const doStuffAsync = async () => {
await setAsyncTimeout(() => {
// Выполнить действия
}, 1000);
await setAsyncTimeout(() => {
// Выполнить дополнительные действия
}, 500);
await setAsyncTimeout(() => {
// Выполнить ещё больше действий
}, 2000);
};
doStuffAsync();
Я также сделал гист: https://gist.github.com/DaveBitter/f44889a2a52ad16b6a5129c39444bb57
Я оставляю этот фрагмент кода здесь для тех, кто хочет выполнить API-запрос (например, получить клиентов) с использованием setTimeout
:
const { data } = await new Promise(resolve => setTimeout(resolve, 250)).then(() => getClientsService())
setName(data.name || '')
setEmail(data.email || '')
Этот код создает новую промис, который завершится через 250 миллисекунд, после чего вызывается функция getClientsService()
для получения данных клиентов. В результате, если данные успешно получены, устанавливаются значения имени и электронной почты. Если имя или email отсутствуют, устанавливается пустая строка.
Использование async/await с циклом forEach
Как передать параметр в колбек setTimeout()?
Синтаксис асинхронной стрелочной функции
Существует ли ссылка на "последнюю" библиотеку jQuery в Google APIs?
Как создать диалог с кнопками "Ок" и "Отмена"