Чому javascript ES6 обіцяє продовження виконання після вирішення?


97

Як я розумію, обіцянка - це те, що може вирішити () або відхилити (), але я здивувався, дізнавшись, що код у обіцянці продовжує виконуватись після виклику рішення або відхилення.

Я вважав, що рішення або відхилення є дружньою до асинхрії версією виходу або повернення, що зупинить виконання негайних функцій.

Чи може хтось пояснити думку, чому наступний приклад інколи показує console.log після виклику вирішення:

var call = function() {
    return new Promise(function(resolve, reject) {
        resolve();
        console.log("Doing more stuff, should not be visible after a resolve!");
    });
};

call().then(function() {
    console.log("resolved");
});

jsbin


12
Питання розумне, але знову ж таки, JS просто виконує одне твердження за іншим, як ти йому кажеш. resolve()це не оператор управління JS, який магічно мав би мати ефект return, це просто виклик функції, і так, виконання продовжується після нього.

Це гарне запитання, і навіть прочитавши всі відповіді, я не впевнений у найкращих практиках ...
Габріель Гленн

Я думаю, що непорозуміння походить від того, що саме ви закінчуєте з разрешением (): обіцянка вирішена відразу після того, як ви телефонуєте на вирішення (), але, як вже говорили інші, це не означає, що функція, яка припинила обіцянку, припинила її мита, тож воно триває до тих пір, поки не досяг «нормального» припинення.
Джузеппе Бертоне

Відповіді:


143

JavaScript має поняття "запустити до завершення" . Якщо помилка не буде видалена, функція виконується, поки не буде досягнуто returnоператора чи його закінчення. Інший код за межами функції не може перешкоджати цьому (якщо, знову ж таки, помилка не буде подана).

Якщо ви хочете resolve()вийти зі своєї функції ініціалізатора, вам слід додати її return:

return new Promise(function(resolve, reject) {
    return resolve();
    console.log("Not doing more stuff after a return statement");
});

Привіт Фелікс - Я думаю, що це лише частина історії - інша частина - resolve()це сама функція асинхронізації. Як ми бачили в іншій (видаленій) відповіді, деякі люди вірять, що дзвінки resolveнегайно запустить будь-які зворотні дзвінки.
Альнітак

3
@Alnitak resolveсам по собі не асинхронний, він повністю синхронний. Хоча з використанням строго API ES6, це не помітно, синхронний чи асинхронний.
Есаїлія

1
@Esailija нормально, можливо, мені було незрозуміло. Деякі люди вважають, що виклик resolveпризведе до негайного виклику будь-яких зареєстрованих зворотних дзвінків , що є частиною поточного стеку викликів. Це неправда, натомість він просто випробовує черги на зворотні дзвінки (і ти маєш рацію, це не асинхронізація, але вона просто робить свою справу і негайно припиняється)
Альнітак

@Alnitak: Я розумію, що ти кажеш. Я просто інтерпретував це так, чому console.logпоказ показується натомість замість того, чому він відображається в такому порядку. Поки що те, що resolveробить і як обіцяє, не має значення для того, як я інтерпретую питання. Але звичайно це все ще важливо знати в контексті обіцянок. Однією з причин я підтримав вашу відповідь :)
Фелікс Клінг

9
@Bergi, у своїй редагуванні ви говорите "return return ();" що здається незвичним. Для того, щоб переконати себе, що там нічого не відбувається, мені довелося прочитати документацію і побачити, що (1) резолінг () не видає нічого наслідкового, і (2) повернення значення зворотного виклику ініціалізації не відповідає видаються використаними. Чи не було б зрозуміліше сказати "вирішити (); повернути;" тим самим уникаючи цього відволікання?
Дон Хетч

19

Зворотні виклики, які будуть викликані, коли ви resolveобіцяєте, все ще вимагаються специфікацією для виклику асинхронно. Це потрібно для забезпечення послідовної поведінки при використанні обіцянок для поєднання синхронних та асинхронних дій.

Тому, коли ви викликаєте resolveзворотний виклик, в черзі , і виконання функції продовжується негайно з будь-яким кодом, що слідує за resolve()викликом.

Тільки після того, як цикл подій JS отримає зворотний контроль, зворотний виклик може бути видалений з черги та фактично викликаний.


1
Черга зворотного дзвінка задокументована в A + Specs або в ES6?
thefourtheye

5
@thefourtheye: Специфікація циклу подій зараз фактично є частиною HTML5 . ES6 визначає внутрішній метод EnqueueJob, який називається , на який посилається .then.
Фелікс Клінг

@thefourtheye: Насправді ES6 також, схоже, визначає черги: people.mozilla.org/~jorendorff/… . Я думаю, що так чи інакше пов'язані з циклом подій.
Фелікс Клінг

@FelixKling дякую за посилання - я знав, що так воно працює, але не міг цитувати главу та вірш
Alnitak

2
@FelixKling це мікрозадачі / макрозадачі, ось частина в специфікації, яка "відкладає" "Коли немає запущеного контексту виконання і стек контексту виконання порожній, реалізація ECMAScript видаляє перший PendingJob з черги завдань і використовує інформацію, що міститься в ньому для створення контексту виконання та запускає виконання пов'язаної з ним абстрактної операції. "
Бенджамін Груенбаум
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.