Разве async не должен возвращать обещание без явной упаковки возвращаемого значения в обещание?

В документации MDN говорится, что

Когда вызывается асинхронная функция, она возвращает обещание. Когда асинхронная функция возвращает значение, обещание будет разрешено с возвращенным значением. Когда асинхронная функция генерирует исключение или какое-либо значение, Promise будет отклонен с брошенным значением.

Следовательно, что я делаю неправильно, учитывая, что await ожидает выполненного обещания?

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
    setTimeout(() => {
        console.log("bar");
        return;
    }, 1000);    
}

function zoo(){
    console.log("zoo");    
}

Согласно тому, что я (ошибочно) понял, он должен сначала регистрировать bar, а затем zoo, но он регистрирует наоборот.

РЕДАКТИРОВАТЬ: Теперь, благодаря разъяснениям @Matt Morgan, я понимаю ошибку, поскольку функция bar() возвращает undefined. Тем не менее, я думал, что один только вызов функции async per se заставит функцию немедленно вернуть неразрешенное обещание и что такое обещание будет разрешено, когда функция async вернет любое значение (даже неопределенное). Но теперь я понимаю, что действительно нужно вернуть обещание самому, объявив его с помощью оператора return Promise. Я думаю, что статья MDN для async, которую я все прочитал, немного сбивает с толку по этой теме (только мое мнение).

Поэтому я мог бы просто изменить свою bar() функцию на:

function bar(){
    return new Promise(function(resolve){
        setTimeout(() => {
            console.log("bar");
            resolve();
        }, 1000);
    });
}

person João Pimentel Ferreira    schedule 05.02.2018    source источник
comment
В только что опубликованной ссылке посмотрите код для resolveAfter2Seconds, затем сравните его с вашим bar (). Заметили что-нибудь?   -  person    schedule 05.02.2018
comment
@Will, я прочитал всю эту статью плюс пример, но я не понимаю, почему вам нужно возвращать Promise, если функция async уже возвращает Promise, просто запустив return;. В документации сказано, что когда вызывается асинхронная функция, она возвращает обещание. Когда асинхронная функция возвращает значение, обещание будет разрешено с возвращенным значением.   -  person João Pimentel Ferreira    schedule 05.02.2018
comment
@Will Поэтому я знаю, как заставить его работать, я просто не понимаю, почему он так работает :)   -  person João Pimentel Ferreira    schedule 06.02.2018
comment
Интересно. Возвраты автоматически заключаются в обещание. Это, безусловно, сокращает шаблон. Аккуратный.   -  person    schedule 06.02.2018
comment
«Тем не менее, я думал, что вызов асинхронной функции сам по себе заставит функцию немедленно вернуть неразрешенное обещание и что такое обещание будет разрешено, когда асинхронная функция вернет любое значение (даже неопределенное)» именно так работают асинхронные функции. Однако bar сразу возвращает undefined. Он не ждет (и не может) ждать окончания тайм-аута. Чтобы это сработало, вы должны сами построить обещание.   -  person Felix Kling    schedule 07.02.2018
comment
@Felix Kling 1) почему bar сразу возвращается, если я return после истечения тайм-аута? 2) если он не может дождаться истечения тайм-аута, зачем мне функция async? Просто чтобы вставить await звонка?   -  person João Pimentel Ferreira    schedule 07.02.2018
comment
1) return не выходит за границы функций. Вы возвращаетесь только внутри обратного вызова. Функция вернется к вызывающему. Но вы не вызываете обратный вызов, браузер вызовет функцию, когда истечет время ожидания. Сам вызов setTimeout немедленно возвращается. Это не блокирует. 2) Если вы явно создаете и возвращаете обещание, асинхронность вам не нужна. Если вы хотите использовать ожидание внутри функции, вам понадобится async.   -  person Felix Kling    schedule 08.02.2018


Ответы (1)


bar() устанавливает тайм-аут и возвращает undefined, что не то же самое, что немедленный запуск оператора регистрации, который запускается по истечении тайм-аута.

Итак, zoo() запускается, а затем, когда истекает тайм-аут (спустя 1000 мс), вы видите «полосу» в консоли.

Вот измененный пример без тайм-аута:

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
   console.log("bar");   
}

function zoo(){
    console.log("zoo");    
}

Без setTimeout вы получите ожидаемый порядок выполнения.

Второй пример, где у вас есть delay() функция, которая оборачивает setTimeout в обещание:

foo();

async function foo(){
    await bar();
    zoo();
}

async function bar(){
   await delay();
   console.log("bar");   
}

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

function zoo(){
    console.log("zoo");    
}

Последний фрагмент ожидает разрешения обещания, поэтому вы видите bar, затем foo.

Вышеупомянутый delay был взят из https://stackoverflow.com/a/39538518/3084820

Вот последний пример, где bar() возвращает "bar". Это означает, что объявление оборачивается в обещание async, а разрешается await внутри foo(). Опять же, вы видите то, что ожидали увидеть.

foo();

async function foo(){
    console.log(await bar());
    zoo();
}

async function bar(){
    return 'bar';   
}

function zoo(){
    console.log("zoo");    
}

Важно понимать, что ваш исходный пример не возвращает значение bar. Он возвращает undefined, и если вы измените исходный код для выхода из системы с возвращенным значением bar(), вы увидите в консоли три вещи:

  1. Немедленно возвращенная форма обещания bar(), которая разрешается в undefined.
  2. Значение zoo из функции zoo().
  3. Наконец, по прошествии 1000 мс вы увидите, что bar зарегистрировано из setTimeout, которое было помещено в очередь.

Попробуйте, посмотрите, что у вас получится.

person Matt Morgan    schedule 05.02.2018
comment
спасибо за ответ, я прекрасно понимаю ваш код. В вашем первом примере нет необходимости async, это просто последовательность команд; во втором примере, который я уже использовал, вы возвращаете Promise, но я не понимаю, почему вам нужно возвращать Promise, если функция async уже возвращает Promise, просто запустив return - person João Pimentel Ferreira; 05.02.2018
comment
Функция bar возвращает undefined. В исходном коде вы устанавливаете тайм-аут, который на самом деле не имеет ничего общего с обещаниями. Чтобы протестировать асинхронное поведение, вам нужно иметь обещание, которое разрешается по истечении времени ожидания, поэтому я включил второй пример. - person Matt Morgan; 05.02.2018
comment
хорошо, но документация MDN говорит, что когда вызывается асинхронная функция, она возвращает обещание. Когда асинхронная функция возвращает значение, обещание будет разрешено с возвращенным значением. Это запутало! - person João Pimentel Ferreira; 05.02.2018
comment
И обещание немедленно разрешается со значением undefined. Я думаю, вы запутались, думая, что выполнение обещания приведет к выполнению оператора console.log. - person Matt Morgan; 05.02.2018
comment
но если есть обещание (меня не волнует цель кода, какое будет значение), не следует ли await ждать разрешения promise? Почему сразу решается, если я просто return позже? - person João Pimentel Ferreira; 05.02.2018
comment
setTimeout использует API браузера, который вставляет обернутую функцию в очередь через определенное время. Это не дает обещания. Ваша функция устанавливает тайм-аут, затем возвращает undefined, и обещание разрешается со значением undefined. - person Matt Morgan; 05.02.2018
comment
Позвольте нам продолжить это обсуждение в чате. - person Matt Morgan; 05.02.2018
comment
Я пометил ваше решение как Решение, но я до сих пор не понимаю, почему функция async не возвращает возвращенное обещание, когда оно просто возвращается. В документации сказано, что когда вызывается асинхронная функция, она возвращает обещание. Когда асинхронная функция возвращает значение, обещание будет разрешено с возвращенным значением. Прошу прощения, если я проявляю настойчивость! - person João Pimentel Ferreira; 06.02.2018
comment
Поэтому я знаю, как заставить его работать, я просто не понимаю, почему он так работает :) - person João Pimentel Ferreira; 06.02.2018
comment
Спасибо еще раз! Теперь я вижу, что он просто возвращает undefined. Я думал, что вызов async per se заставит функцию немедленно вернуть неразрешенное обещание и что такое обещание будет разрешено, когда функция async вернет любое значение (даже неопределенное). Но теперь я вижу, что вам действительно нужно вернуть обещание самостоятельно, объявив его с помощью оператора return Promise. Я думаю, что статья MDN для async, которую я все прочитал, немного сбивает с толку по этой теме (только мое мнение). В любом случае, большое спасибо! - person João Pimentel Ferreira; 06.02.2018