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

асинхронная функция main () {
    var value = await Promise.resolve ('Привет!');
    console.log ('внутри:' + значение);
    возвращаемое значение;
}

var text = main ();
console.log ('за пределами:' + текст);

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

> снаружи: [обещание объекта]

> внутри: Привет

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

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

Ответы (11)

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

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

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

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

  2. Используйте , затем и catchили

  3. (Скоро!) Используйте верхний уровень await, предложение, достигшее стадии 3 в процессе что позволяет использовать на верхнем уровне 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 - , затем и поймать

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

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

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

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

Again notice we're registering a rejection handler. But in this form, be sure that neither of your then callbacks doesn't throw any errors, nothing is registered to handle them.

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

Вы не можете использовать await на верхнем уровне немодульного скрипта, но верхнего уровня await предложение (Stage 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
}

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

Мне нравится этот умный синтаксис для выполнения асинхронной работы из точки входа

void async function main () {
  ждать doSomeWork ()
  ожидание doMoreWork ()
} ()

В других решениях отсутствовали некоторые важные детали для соответствия POSIX:

Вам необходимо ...

  • Сообщать о статусе выхода 0 в случае успеха и ненулевом в случае неудачи.
  • Выдавать ошибки в выходной поток stderr.
#! / Usr / bin / env узел

асинхронная функция main () {
 // ... ждать чего-нибудь ...
}

// POSIX-совместимые приложения должны сообщать о статусе выхода
главный()
    .then (() => {
        process.exit (0);
    })
    .catch (err => {
        console.error (ошибка); // Записывает в stderr
        process.exit (1);
    });

Если вы используете синтаксический анализатор командной строки, например commander, вам может не понадобиться main ().

Пример:

#! / Usr / bin / env узел

командир импорта из 'командир'

const program = новый командир.Command ();

программа
  .version ("0.0.1")
  .command ("некоторые-cmd")
  .arguments ("")
  .action (async (arg1: string) => {
    // запускаем асинхронное действие
  });

program.parseAsync (process.argv)
  .then (() => {
    process.exit (0)
  })
  .catch (err => {
    console.error (сообщение об ошибке || ошибка);
    если (err.stack) console.error (err.stack);
    process.exit (1);
  });

В NodeJS 14.8+ вы можете использовать модуль ожидания верхнего уровня (решение №3). Вы также можете переименовать .js в .mjs (модуль ES) вместо .js (.cjs CommonJS).

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

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

Верхний уровень await перешел на этап 3, поэтому ответ на ваш вопрос Как я могу использовать async / await на верхнем уровне? is просто добавить await вызов main ():

асинхронная функция main () {
    var value = await Promise.resolve ('Привет!');
    console.log ('внутри:' + значение);
    возвращаемое значение;
}

var text = await main ();
console.log ('за пределами:' + текст)

Или просто:

const text = await Promise.resolve ('Привет!');
console.log ('за пределами:' + текст)

Совместимость

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

Большинство приведенных выше ответов немного устарели или очень подробны, поэтому вот быстрый пример для узла 14 и далее.

Создайте файл с именем runme.mjs:

import * as util from "util";
import { exec as lameExec } from "child_process";
const exec = util.promisify(lameExec);
const log = console.log.bind(console);

// Top level await works now
const { stdout, stderr } = await exec("ls -la");
log("Output:\n", stdout);
log("\n\nErrors:\n", stderr);

Выполнить узел runme.mjs

Output:
 total 20
drwxr-xr-x  2 mike mike 4096 Aug 12 12:05 .
drwxr-xr-x 30 mike mike 4096 Aug 12 11:05 ..
-rw-r--r--  1 mike mike  130 Aug 12 12:01 file.json
-rw-r--r--  1 mike mike  770 Aug 12 12:12 runme.mjs



Errors:

Теперь вы можете использовать ожидание верхнего уровня в Node v13.3.0

импортировать axios из "axios";

const {данные} = ожидание axios.get ("https://api.namefake.com/");
console.log (данные);

запустить его с - harmony-top-level-await flag

узел --harmony-top-level-await index.js

Реальным решением этой проблемы является другой подход.

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

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

* 10.0001 100002 *

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

import {application} from './server'

application();

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

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

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

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

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

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

The major thing to note, is that the resulting function is NOT an async function. So you cannot use the term await directly inside of it!

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

  1. Не используйте await напрямую внутри функции
  2. Не использовать ждать

Вариант 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!'));

Было бы интересно увидеть поддержку добавления узла для верхнего уровня await!

Узел -
Вы можете запустить node --experimental-repl-await в REPL. Я не уверен в написании скриптов.

Deno -
В Deno он уже встроен.

2022 WebDevInsider