Використання await поза асинхронною функцією


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, однак я отримав помилку типу без конкретних деталей.

Чому ви не можете функції поза області видимості функції? Я знаю, що це обіцянка про цукор, тому її потрібно прикувати, але чому я можу чекати чергової обіцянки, а зовні не можу ?awaitasyncthencountPlayerawait isInLobby


Чи можете ви показати нам, де ви робили await isInLobby(), і як inLobbyце використовується? Крім того, де / як countPlayerназивається?
Бергі,

@Bergi Я пов’язав своє репо для реального контексту. Занадто багато коду, щоб поставити питання
Sterling Archer

Я не бачу, де з цим проблема (можливо, ви вже оновили репо)? Якщо ви посилаєтесь на isInLobby().then( … countPlayer().then …частину, рішення є тривіальним: просто зробіть функцію, в якій містяться ці виклики (ту, яку (req, res) =>викликана) async.
Бергі,

@Bergi справа не в тому, що вона була зламана, вона працює як є. Я просто не розумів, чому очікування на найвищому рівні - це не річ. Виявляється, він просто ще не існує, не застосувавши весь модуль як функцію асинхронізації
Sterling Archer

Але вам навіть не потрібен верхній рівень await для вашого коду? Ось чому мені було цікаво, що ви прийняли відповідь, яка насправді не стосується проблеми у питанні.
Бергі

Відповіді:


73

Верхній рівень 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стосується функції, що робить її недоступною?
Sterling Archer

@SterlingArcher так, це вимагало б, щоб ви перенесли туди весь свій код (в основному, зробивши його "найвищим рівнем"). Це просто альтернатива використанню .then()(вибачте, мала б зробити це трохи зрозумілішим).
robertklep

не погоджуюсь, що користувач data.js "заблокований", проте під час завантаження самого data.js та всіх його залежностей це поняття "блокування" саме по собі не є поганим. верхній рівень await можна розглядати як завантаження деякої залежності, яку, мабуть, потрібно мати перед тим, як використання буде «випущено».
Ібрагім бен Салах

125

Звичайно, це завжди є:

(async () => {
    await ...

    // all of the script.... 

})();
// nothing else

Це робить швидку функцію з асинхронізацією, де ви можете використовувати await. Це економить вам необхідність створювати асинхронну функцію, яка чудова! // кредити Silve2611


3
Будь ласка, детальніше поясніть і поясніть, чому це працює.
Пол Назад,

12
дуже дурно голосувати проти цієї відповіді. Це робить швидку функцію з асинхронізацією, де ви можете використовувати await. Це економить вам необхідність створювати асинхронну функцію, яка чудова!
Silve2611

55
Не вирішує проблему, оскільки вам все ще потрібна awaitця анонімна функція, яка знову ж не працює поза функціями.
Майкл

так як би це обробляло стан помилки? він також потрапляє всередину коду очікування?
Мануель Ернандес,

Tks це велика допомога, спеціально для обіцянки зворотного виклику після отримання API для входу, який іде ідеально відповісти, щоб отримати сховище getItem
tess hsu

9

Ще краще поставити додаткову крапку з комою перед блоком коду

;(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)
})()

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


20
Помилка не в переформатувальнику. Без крапки з комою саме так ваша функція буде проаналізована під час виконання, і ви отримаєте помилку, розрив рядка або відсутність розриву рядка. Правильним рішенням у вашому прикладі було б додати крапку з комою після, add(1);а не перед функцією асинхронізації.
Привид

11
Крім того, я, чесно кажучи, не розумію, як це пов'язано з актуальним питанням.
Привид

Так, ви праві. Також час виконання потребує крапки з комою.
Viliam Simko


1

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

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

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

Зауважте, є тонкощі: верхній рівень await працює лише на верхньому рівні модуля, а файли вважаються модулями лише тоді, коли TypeScript знаходить імпорт чи експорт. У деяких основних випадках вам може знадобитися виписати експорт {} як шаблон, щоб переконатися в цьому.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.