Как асинхронно запускать код из асинхронной функции?

#javascript #asynchronous #async-await

Вопрос:

Если вы вызываете асинхронную функцию из несинхронного кода, все, что после ожидания выполняется асинхронно по отношению к вызывающему объекту:

 async function asyncInner() {  console.log('before asyncInner');  await Promise.resolve()  console.log('after asyncInner'); }  console.log('start'); asyncInner() console.log('end'); 

Другими словами, это печатает:

 start before asyncInner end after asyncInner  

Но если вы вызовете эту асинхронную функцию из другой асинхронной функции:

 async function asyncInner() {  console.log('before asyncInner');  await Promise.resolve()  console.log('after asyncInner'); }  async function asyncOuter() {  console.log('before asyncOuter');  await asyncInner();  console.log('after asyncOuter'); }  console.log('start'); asyncOuter() console.log('end'); 

вы получаете:

 start before asyncOuter before asyncInner end after asyncInner after asyncOuter  

Другими словами, внутренний метод завершается синхронно по отношению к внешнему методу. Почему это и как это можно было написать так, чтобы asyncOuter завершить до asyncInner этого ?

(Я знаю, что мог бы использовать a setTimeout для этого, но я бы предпочел избегать использования обратных вызовов, потому что одна из приятных вещей в асинхронности — избегать ада обратного вызова.)

Комментарии:

1. the inner method completes synchronously Нет, это не так, может быть, это вы запутались.

2. Потрясающая наглядная демонстрация поведения, которое вы хотите лучше понять. Два запроса: 1. Не могли бы вы уточнить, какой именно вывод вы хотели бы получить, включая все 6 распечаток? 2. Не могли бы вы переформулировать свой вопрос «почему» в » что » или «как»? Это может помочь вам получить более четкий ответ на то, что вы хотели бы знать (например. «Что конкретно заставляет асинхронную функцию, вызываемую в другой асинхронной функции, вести себя синхронно?»)

3. @Кит: Я вижу, что перепутал «синхронно» с «ждал».

Ответ №1:

Другими словами, внутренний метод завершается синхронно по отношению к внешнему методу. Почему это и как это может быть написано так, чтобы asyncOuter завершался раньше asyncInner?

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

 async function asyncInner() {  console.log('before asyncInner');  await Promise.resolve()  console.log('after asyncInner'); }  async function asyncOuter() {  console.log('before asyncOuter');  asyncInner(); // lt;--------- removed the await  console.log('after asyncOuter'); }  console.log('start'); asyncOuter() console.log('end'); 

Комментарии:

1. Спасибо за объяснение. Я вижу, что я путал «синхронно» и «ждал». Я понимаю, что на самом деле я хочу сказать: «позвольте этой процедуре завершиться, а затем вызовите последующий код». Другими словами, просто синтаксический сахар для setTimeout, и ожидание в основном не связано с этим.

Ответ №2:

@Кит: Я вижу, что перепутал «синхронно» с «ждал».

Даже тогда это как-то расплывчато. await в Javascript действительно есть какой-то причудливый синтаксический сахар promise.then(result =gt; {}) , поэтому на самом деле ничего не ждет, просто ад обратного вызова преобразуется во что-то, что выглядит синхронно, и поэтому с ним намного проще рассуждать.

Это могло бы иметь больше смысла , если бы мы удалили await и сделали это старомодным способом использования thenable .

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

 function asyncInner() {  console.log('before asyncInner');  return Promise.resolve().then(r =gt;   console.log('after asyncInner')); }  function asyncOuter() {  console.log('before asyncOuter');  return asyncInner().then(() =gt;   console.log('after asyncOuter')); }  console.log('start'); asyncOuter() console.log('end'); 

Комментарии:

1. Это действительно облегчает отслеживание за счет скобок и других проблем с синтаксисом обратного вызова.