Как я могу использовать async/wait на верхнем уровне?

Я перешел через async/wait, и, пройдя несколько статей, я решил сам проверить. Тем не менее, я, похоже, не могу обернуть вокруг себя, почему это не работает:

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = main();  
console.log('outside: ' + text)

Консоль выводит следующее (node v8.6.0):

> снаружи: [object Promise]

> внутри: Эй там

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

Можно ли использовать значение, возвращаемое внутри функции, без использования .then() после main()?

Ответ 1

  Кажется, я не могу понять, почему это не работает.

Потому что main возвращает обещание; все функции async выполняют.

На верхнем уровне вы должны либо:

  1. Используйте функцию верхнего уровня async, которая никогда не отклоняет (если вы не хотите ошибок "необработанное отклонение"), или

  2. Используйте then и catch или

  3. (Скоро!) Используйте верхнего уровня await, предложение, которое достигло стадии 3 в процессе process, которое позволяет использовать на верхнем уровне await в модуль.

# 1 - функция верхнего уровня async, которая никогда не отклоняет

(async () => {
    try {
        var text = await main();
        console.log(text);
    } catch (e) {
        // Deal with the fact the chain failed
    }
})();

Обратите внимание на catch; вы должны обрабатывать обещания отклонения/асинхронные исключения, так как больше ничего не происходит; у вас нет звонящего, чтобы передать их. Если вы хотите, вы можете сделать это в результате вызова через функцию catch (а не синтаксис try/catch):

(async () => {
    var text = await main();
    console.log(text);
})().catch(e => {
    // Deal with the fact the chain failed
});

... что более кратко (мне нравится по этой причине).

Или, конечно же, не обрабатывайте ошибки, а просто допускайте ошибку "необработанный отказ".

# 2 - then и catch

main()
    .then(text => {
        console.log(text);
    })
    .catch(err => {
        // Deal with the fact the chain failed
    });

Обработчик catch будет вызван, если в цепочке или в вашем обработчике then возникнут ошибки. (Убедитесь, что ваш обработчик catch не выдает ошибки, так как для их обработки не зарегистрировано ничего.)

Или оба аргумента then:

main().then(
    text => {
        console.log(text);
    },
    err => {
        // Deal with the fact the chain failed
    }
);

Снова обратите внимание, что мы регистрируем обработчик отклонения. Но в этой форме убедитесь, что ни один из ваших обратных вызовов then не выдает никаких ошибок, ничего не зарегистрировано для их обработки.

# 3 верхнего уровня await в модуле

Вы не можете использовать await на верхнем уровне немодульного скрипта, но предложение верхнего уровня await (Этап 3) позволяет использовать его на верхний уровень модуля. Это похоже на использование функции-обертки верхнего уровня async (№1 выше) в том смысле, что вы не хотите, чтобы ваш код верхнего уровня отклонял (выдавал ошибку), потому что это приведет к необработанной ошибке отклонения. Поэтому, если вы не хотите, чтобы этот необработанный отказ отклонялся, когда что-то пошло не так, как в случае С# 1, вы захотите обернуть свой код в обработчик ошибок:

// In a module, once the top-level 'await' proposal lands
try {
    var text = await main();
    console.log(text);
} catch (e) {
    // Deal with the fact the chain failed
}

Ответ 2

Фактическое решение этой проблемы - это подход к ней по-разному.

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

Решение состоит в том, чтобы убедиться, что на самом верхнем уровне вашего приложения существует только одна заявка на JavaScript. Если у вас есть только один оператор в верхней части приложения, то вы можете использовать async/await в любой другой точке в любом месте (естественно, при условии соблюдения нормальных синтаксических правил)

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

Это то, на что должен выглядеть верхний уровень вашего приложения:

import {application} from './server'

application();

Ответ 3

Top-Level await переместились на 3 стадию, так что ответ на ваш вопрос Как я могу использовать асинхронный/поджидает на высшем уровне? это просто добавить await вызова main():

async function main() {  
    var value = await Promise.resolve('Hey there');
    console.log('inside: ' + value);
    return value;
}

var text = await main();  
console.log('outside: ' + text)

Или просто:

const text = await Promise.resolve('Hey there');
console.log('outside: ' + text)

Имейте в виду, что он по-прежнему доступен только в [email protected].

Ответ 4

Чтобы дать некоторую дополнительную информацию поверх текущих ответов:

Содержимое файла node.js в настоящее время объединяется в виде строки, чтобы сформировать тело функции.

Например, если у вас есть файл test.js:

// Amazing test file!
console.log('Test!');

Затем node.js тайно объединит функцию, которая выглядит следующим образом:

function(require, __dirname, ... a bunch more top-level properties) {
  // Amazing test file!
  console.log('test!');
}

Главное, на что следует обратить внимание, это то, что результирующая функция НЕ является асинхронной функцией. Таким образом, вы не можете использовать термин await прямо внутри него!

Но, скажем, вам нужно работать с обещаниями в этом файле, тогда есть два возможных метода:

  1. Не используйте await непосредственно внутри функции
  2. Не пользуйтесь, await

Вариант 1 требует, чтобы мы создали новую область (и ЭТА область может быть async, потому что у нас есть контроль над ней):

// Amazing test file!
// Create a new async function (a new scope) and immediately call it!
(async () => {
  await new Promise(...);
  console.log('Test!');
})();

Вариант 2 требует от нас использования объектно-ориентированного API обещаний (менее привлекательная, но в то же время функциональная парадигма работы с обещаниями)

// Amazing test file!
// Create some sort of promise...
let myPromise = new Promise(...);

// Now use the object-oriented API
myPromise.then(() => console.log('Test!'));

Я лично надеюсь, что, если это будет работать, node.js по умолчанию объединит код в async функцию. Это избавило бы от этой головной боли.

Ответ 5

Поскольку main() работает асинхронно, он возвращает обещание. Вы должны получить результат в методе then(). И поскольку then() возвращает обещание, вы должны вызвать process.exit() чтобы завершить программу.

main()
   .then(
      (text) => { console.log('outside: ' + text) },
      (err)  => { console.log(err) }
   )
   .then(() => { process.exit() } )