Использование ожидания вне асинхронной функции

86

Я пытался связать две асинхронные функции вместе, потому что первая имела параметр условного возврата, из-за которого вторая либо запускалась, либо выходила из модуля. Однако я обнаружил странное поведение, которое не смог найти в спецификациях.

async function isInLobby() {
    //promise.all([chained methods here])
    let exit = false;
    if (someCondition) exit = true;
}

Это убогий фрагмент моего кода (вы можете увидеть полный объем здесь ), который просто проверяет, находится ли игрок в лобби, но это не имеет значения.

Далее у нас есть асинхронная функция.

async function countPlayer() {
    const keyLength = await scardAsync(game);
    return keyLength;
}

Эту функцию не нужно запускать, если exit === true.

Я пытался сделать

const inLobby = await isInLobby();

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

Почему вы не можете awaitвыполнить asyncфункцию за пределами функции? Я знаю, что это сахарное обещание, поэтому оно должно быть привязано к нему, thenно почему внутри countPlayerя могу ждать другого обещания, а снаружи я не могу await isInLobby?

Стерлинг Арчер
источник
Вы можете показать нам, где вы это сделали await isInLobby()и как inLobbyиспользуется? Кроме того, где / как countPlayerназывается?
Берги
@Bergi Я связал свое репо с фактическим контекстом. Слишком много кода, чтобы задать вопрос
Стерлинг Арчер,
Я не вижу, в чем проблема (может быть, вы уже обновили репо)? Если вы обратитесь к isInLobby().then( … countPlayer().then …части, решение тривиально: просто создайте функцию, в которой содержатся эти вызовы (ту (req, res) =>) async.
Берги
@Bergi проблема не в том, что он сломан, он работает как есть. Я просто не понимал, почему ожидание на высшем уровне не было делом. Оказывается, этого просто еще нет, если не рассматривать весь ваш модуль как асинхронную функцию
Стерлинг Арчер,
Но вам вообще не нужен код верхнего уровня await ? Вот почему я подумал, что вы приняли ответ, который на самом деле не имеет отношения к проблеме в вопросе.
Берги

Ответы:

74

Верхний уровень awaitне поддерживается. В комитете по стандартам есть несколько дискуссий о том, почему это так, например, этот вопрос Github .

На Github также есть статья о том, почему ожидание верхнего уровня - плохая идея. В частности, он предполагает, что если у вас есть такой код:

// data.js
const data = await fetch( '/data.json' );
export default data;

Теперь любой импортируемый файл data.jsне будет выполняться до завершения выборки, поэтому вся загрузка вашего модуля теперь заблокирована. Из-за этого очень сложно рассуждать о порядке модулей приложения, поскольку мы привыкли, что Javascript верхнего уровня выполняется синхронно и предсказуемо. Если бы это было разрешено, узнать, когда функция будет определена, будет сложно.

Я считаю , что для вашего модуля плохая практика - иметь побочные эффекты, просто загружая его. Это означает, что любой потребитель вашего модуля получит побочные эффекты, просто потребовав ваш модуль. Это сильно ограничивает возможности использования вашего модуля. Верхний уровень, awaitвероятно, означает, что вы читаете какой-либо API или вызываете какой-либо сервис во время загрузки. Вместо этого вам следует просто экспортировать асинхронные функции, которые потребители могут использовать в своем собственном темпе.

Энди Рэй
источник
Хорошая ссылка, спасибо. Жаль, что там нет поддержки на высшем уровне. Я надеюсь, что это так. В настоящее время я должен вложить сюда свои обещания, и это очень плохая практика, и мне это не нравится. :( Спасибо.
Стерлинг Арчер
@SterlingArcher в качестве альтернативы используйте асинхронный IIFE:void async function() { const inLobby = await isInLobby() }()
robertklep
@robertklep не будет ли эта inLobbyфункция недоступна для функции?
Стерлинг Арчер
@SterlingArcher: да, это потребовало бы, чтобы вы переместили весь свой код туда (в основном, делая его «верхним уровнем»). Это просто альтернатива использованию .then()(извините, я должен был прояснить это).
robertklep
не согласен, пользователь data.js «заблокирован», тем не менее, при загрузке самого data.js и всех его зависимостей это понятие «блокировки» само по себе неплохо. ожидание верхнего уровня можно рассматривать как загрузку некоторой зависимости, которая, по-видимому, должна иметься до того, как использование будет «освобождено».
Ибрагим бен Салах
128

Конечно, всегда есть это:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

Это обеспечивает быструю работу с async, где вы можете использовать ожидание. Это избавляет вас от необходимости создавать асинхронную функцию, и это здорово! // кредиты Silve2611

Digerati-стратегии
источник
3
Пожалуйста, уточните подробнее и объясните, почему это работает.
Paul Back
12
Голосовать против этого ответа очень глупо. Он выполняет быструю функцию с помощью async, где вы можете использовать ожидание. Это избавляет вас от необходимости создавать асинхронную функцию, и это здорово!
Silve2611
55
Не решает проблему, поскольку вам все еще нужна awaitэта анонимная функция, которая, опять же, не работает извне функций.
Майкл
так как это обработать состояние ошибки? он также попадает в код ожидания?
Мануэль Эрнандес
Спасибо, это большая помощь, особенно для обещания обратного вызова после получения API входа в систему, который отлично отвечает, чтобы получить хранилище getItem
tess hsu
10

Еще лучше поставить дополнительную точку с запятой перед блоком кода

;(async () => {
    await ...
})();

Это не позволяет автоматическому форматированию (например, в vscode) перемещать первую круглую скобку в конец предыдущей строки.

Проблема может быть продемонстрирована на следующем примере:

const add = x => y => x+y
const increment = add(1)
(async () => {
    await ...
})();

Без точки с запятой это будет переформатировано как:

const add = x => y => x+y
const increment = add(1)(async () => {
  await Promise(1)
})()

что, очевидно, неверно, потому что он назначает асинхронную функцию в качестве yпараметра и пытается вызвать функцию из результата (который на самом деле является странной строкой '1async () => {...}')

Вильям Симко
источник
20
Дело не в переформаторе. Без точки с запятой ваша функция будет анализироваться во время выполнения, и вы получите ошибку, разрыв строки или отсутствие разрыва строки. Правильным решением в вашем примере было бы добавить точку с запятой после, add(1);а не перед функцией async.
Призрак Мадары
11
Кроме того, я, честно говоря, не понимаю, как это связано с рассматриваемым вопросом.
Призрак Мадары
Да, ты прав. Также среде выполнения нужна точка с запятой.
Вильям Симко
1

вы можете выполнить ожидание верхнего уровня, так как typescript 3.8
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-top-level-await
Из сообщения:
Это потому, что ранее в В JavaScript (как и в большинстве других языков с аналогичной функцией) ожидание было разрешено только в теле асинхронной функции. Однако с ожиданием верхнего уровня мы можем использовать ожидание на верхнем уровне модуля.

const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};

Обратите внимание на тонкость: await верхнего уровня работает только на верхнем уровне модуля, а файлы считаются модулями только тогда, когда TypeScript находит импорт или экспорт. В некоторых базовых случаях вам может потребоваться написать export {} в качестве шаблона, чтобы убедиться в этом.

Ожидание верхнего уровня может работать не во всех средах, в которых вы могли ожидать на этом этапе. В настоящее время вы можете использовать ожидание верхнего уровня только тогда, когда целевой параметр компилятора - es2017 или выше, а модуль - esnext или system. Поддержка в нескольких средах и сборщиках может быть ограничена или может потребоваться включение экспериментальной поддержки.

ТОПОР
источник