Почему можно попытаться поймать вызов async-await? - PullRequest
0 голосов
/ 21 сентября 2018

В JavaScript есть общий анти-шаблон:

function handleDataClb(err, data) {
    if(!data) throw new Error('no data found');
    // handle data... 
} 

function f() {
    try {
        fs.readFile('data', 'utf8', handleDataClb);
    } catch(e) {
        // handle error... 
    }
}

Эта попытка перехвата в f не будет перехватывать ошибки в handleDataClb, так как обратный вызов вызывается на более поздней стадии и в контексте, гдеtry-catch больше не виден.

Теперь в JavaScript async-await реализован с использованием генераторов, обещаний и сопрограмм, например:

// coroutine example 
co(function* doTask() {
    try {
        const res1 = yield asyncTask1(); // returns promise
        const res2 = yield asyncTask2(); // returns promise
        return res1 + res2;
    } catch(e) {
        // handle error... 
    }
});

// async-await example
async function doTask() {
    try {
        const res1 = await asyncTask1(); // returns promise
        const res2 = await asyncTask2(); // returns promise
        return res1 + res2;
    } catch(e) {
        // handle error... 
    }
}

Таким образом, try-catch работает, который часто упоминается как одно большое преимущество async-await перед обратными вызовами.

Почему и как работает catch?Как сопрограмме aka async удается выбросить ошибку в try-catch, когда один из вызовов asyncTask приводит к отклонению обещания?

EDIT: как указывали другие, как способМеханизм JavaScript, реализующий оператор await, может сильно отличаться от реализации чистого JavaScript, используемой такими транспиляторами, как Babel, и показанной выше как coroutine example.Поэтому, чтобы быть более конкретным: как это работает с использованием родного JavaScript?

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

Почему и как работает catch?Как сопрограмме aka async удается выдать ошибку в try-catch? * Выражение

A yield или await может иметь 3 различных результата:

  • Он может вычисляться как простое выражение для значения результата этого
  • Он может вычисляться как оператор throw, вызывая исключение
  • Он может вычисляться как оператор return, вызываятолько finally операторы, которые должны быть оценены перед завершением функции

На приостановленном генераторе это может быть достигнуто путем вызова либо .next(), .throw() или .return() методы.(Конечно, есть и 4-й возможный результат, который никогда не возобновится).

… когда один из вызовов asyncTask приводит к отклонению обещания?

The await значение ed будет Promise.resolve() d для обещания, тогда метод .then() будет вызван для него с двумя обратными вызовами: когда обещание выполняется, сопрограмма возобновляется с нормальным значением (результат обещания), и когда обещание отклоняется, сопрограмма возобновляется с неожиданным завершением (исключение - причина отклонения).

Вы можете посмотреть код библиотеки co или вывод транспортера - буквально вызывает gen.throw от обратного вызова об отказе от обещания .

0 голосов
/ 21 сентября 2018

async функции

Асинхронная функция возвращает обещание, которое разрешается значением, возвращаемым телом функции, или отклоняется ошибкой, выданной в теле.

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

Ошибки, выданные await, могут быть перехвачены блоками try-catch внутри функции async, вместо того, чтобы позволить им распространяться вверх по стеку выполнения и отклонить обещание, возвращаемое вызовом функции async.

Оператор await также сохраняет контекст выполнения перед возвратом в цикл событий, чтобы разрешить выполнение операций обещания.При внутреннем уведомлении об урегулировании ожидаемого обещания он восстанавливает контекст выполнения перед продолжением. Блок

try/catch, установленный в контексте выполнения функции async, не изменяется и не становится неэффективным просто потому, что контекст имеетбыл сохранен и восстановлен await.

As aside

"async-await реализован с использованием генераторов, обещаний и сопрограмм"

может быть частью того, как Babel переносит использование async функции и await оператора, но нативные реализации могут быть реализованы более напрямую.


Функции генератора (обновление)

Контекст выполнения функции генератора сохраняется во внутреннем слоте [[Generator Context]] связанного с ним объекта генератора.( ECMA 2015 25.3.2 )

Выражения выхода удаляют контекст выполнения генератора из верхней части стека контекста выполнения ( 25.3.3.5 из ES6 / ECMAScript 2015 )

Возобновление функции генератора восстанавливает контекст выполнения функции из слота [[Generator Context]] объекта генератора.

Следовательно, функции генератора эффективно восстанавливают предыдущий контекст выполнения, когда возвращается выражение yield.

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

Выдача ошибки с помощью Generator.prototype.throw() выдает ошибку в функции генератора, происходящую из yield expression, который в последний раз передал управление от функции генератора.Эта ошибка может быть зафиксирована на try-catch, как и для обычных ошибок.(Ссылка на MDN с использованием throw () , ECMA 2015 25.3.3.4

Сводка

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...