Перехват ошибок в промисах JavaScript с помощью первого уровня try catch

Итак, я хочу, чтобы мой первый уровень catch обрабатывал ошибку. Есть ли способ распространить мою ошибку до первого улова?

Справочный код, не работает (пока):

Promise = require('./framework/libraries/bluebird.js');

function promise() {
    var promise = new Promise(function(resolve, reject) {
        throw('Oh no!');
    });

    promise.catch(function(error) {
        throw(error);
    });
}

try {   
    promise();
}
// I WANT THIS CATCH TO CATCH THE ERROR THROWN IN THE PROMISE
catch(error) {
    console.log('Caught!', error);
}

person Kirk Ouimet    schedule 27.07.2014    source источник
comment
Просто чтобы вы знали, вы просите путаницы, чтобы иметь две разные вещи с именами promise (функция и переменная), не говоря уже о встроенной Promise. Я уверен, что вы могли бы заставить его работать, но зачем так запутывать свой код?   -  person jfriend00    schedule 27.07.2014


Ответы (5)


С новым синтаксисом async/await вы можете добиться этого. Обратите внимание, что на момент написания это поддерживается не всеми браузерами, вам, вероятно, потребуется транспилировать код с помощью babel ( или что-то подобное).

// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue() {
  if (somethingIsNotOk) {
    throw new Error('uh oh');
  } else {
    return 'Yay!';
  }
}

async function() {
  try {
    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);
  } catch (error) {
    console.error(error);
  }
}
person Edo    schedule 03.02.2017
comment
если функция возвращает обещание, нам нужно объявить этот метод как асинхронный. - person j10; 19.07.2017
comment
Вам нужно только ключевое слово async, если вы хотите использовать await внутри этой функции. Побочным эффектом использования ключевого слова async будет то, что обещание вернет обещание. Если вы хотите дождаться результата функции X, которая для начала возвращает промис, асинхронность функции X не будет иметь никакого эффекта. - person Edo; 20.07.2017
comment
отсутствует: объявление Promise.reject(err); внутри catch stackoverflow.com/a/37993829/4933053 - person qrtLs; 31.03.2018
comment
@ItsmeJulian Это зависит от того, как вы хотите с этим справиться. Если вы просто хотите войти внутрь улова, вам не нужно будет всплывать сообщение об ошибке. Кроме того, я думаю, что я бы предпочел throw err внутри улова, если вы хотите его поднять. - person Edo; 03.04.2018

Вы не можете использовать операторы try-catch для обработки исключений, генерируемых асинхронно, так как функция возвращается до того, как возникнет какое-либо исключение. Вместо этого вы должны использовать методы promise.then и promise.catch, которые представляют собой асинхронный эквивалент оператора try-catch. (Или используйте синтаксис async/await, указанный в ответе @Edo.)

Что вам нужно сделать, так это вернуть обещание, а затем связать с ним еще .catch:

function promise() {
    var promise = new Promise(function(resolve, reject) {
        throw('Oh no!');
    });

    return promise.catch(function(error) {
        throw(error);
    });
}

promise().catch(function(error) {
    console.log('Caught!', error);
});

Обещания являются цепочками, поэтому, если обещание повторно выдает ошибку, оно будет делегировано следующему .catch.

Кстати, вам не нужно использовать круглые скобки вокруг операторов throw (throw a совпадает с throw(a)).

person Qantas 94 Heavy    schedule 27.07.2014
comment
Неужели нет другого пути? - person Kirk Ouimet; 27.07.2014
comment
@Kirk: Я бы сделал это только в том случае, если вы не можете редактировать эту функцию promise, так как гораздо более последовательно использовать ее по одной. В этом случае я отредактировал свой ответ, добавив ссылку на возможное решение. - person Qantas 94 Heavy; 27.07.2014
comment
Что бы это ни стоило, вы определенно не должны не использовать метод в моем ответе, и вместо этого вы должны использовать обещания. Промисы уже решают эту проблему и обеспечивают безопасность броска. Вы можете просто опустить promise.catch, на самом деле, весь вышеприведенный код можно реорганизовать в Promise.reject("oh no") (хотя вы должны всегда отклонять с ошибками). Затем вы можете сделать Promise.reject("oh no").catch(function(e){ console.log(e); });, который регистрирует О нет. Безопасность броска на самом деле является одним из самых сильных аргументов в пользу обещаний наряду с цепной связью и компонуемостью. - person Benjamin Gruenbaum; 27.07.2014
comment
@BenjaminGruenbaum: я предполагал, что это был просто пример макета, основанный на каком-то другом коде, который иногда (повторно) выдает ошибку, а не то, что это был точный код. В любом случае, я отредактировал свой ответ, чтобы сильнее подчеркнуть использование обещаний. - person Qantas 94 Heavy; 27.07.2014

Нет! Это совершенно невозможно, так как промисы по своей природе асинхронны. Предложение try-catch завершит выполнение, когда возникнет исключение (и путешествие во времени все еще не будет изобретено).

Вместо этого возвращайте обещания из всех ваших функций и подключайте к ним обработчик ошибок.

person Bergi    schedule 27.07.2014

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

function doSomeWork() {
  return Promise.try(function() {

    return request.get(url).then(function(response) {
      // ... do some specific work
    });

  }).catch(function(err) {
    console.log("Some specific work failed", err);
    throw err; // IMPORTANT! throw unless you intend to suppress the error
  });
}

Преимущество этого метода (Promise.try/catch) заключается в том, что вы запускаете/обеспечиваете цепочку промисов без требования разрешения/отклонения, которое можно легко пропустить и создать кошмар отладки.

person kingdango    schedule 17.09.2016

Чтобы расширить ответ edo, если вы хотите поймать ошибки асинхронной функции, которые вы не хотите ждать. Вы можете добавить оператор ожидания в конце вашей функции.

(async function() {
  try {
    const asyncResult = someAsyncAction();

    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);

    await asyncResult;
  } catch (error) {
    console.error(error);
  }
})();

Если someAsyncAction терпит неудачу, инструкция catch обработает ее.

person Jonathan Felchlin    schedule 28.11.2017
comment
Просто примечание: это все равно остановит завершение внешней функции до тех пор, пока asyncResult не завершится (либо с ошибкой, либо успешно). Если вы вообще не хотите ждать asyncResult, вероятно, будет проще связать .catch с someAsyncAction(). - person Edo; 23.10.2018
comment
Я не уверен, что вы имеете в виду, когда говорите, что это остановит завершение внешней функции, но не заблокирует цикл обработки событий. Как написано, выполнение любого кода, размещенного после внешней функции, не будет заблокировано разрешением промисов внутри функции. Единственное, что вы выиграете, добавив .catch к someAsyncAction(), это то, что обработка ошибок будет связана синхронно. Это позволит избежать посторонних сообщений UnhandledPromiseRejectionWarning, если someAsyncAction будет отклонено до того, как будет достигнута строка await asyncResult. - person Jonathan Felchlin; 24.10.2018
comment
Я имею в виду, что если какой-то код await выполняет внешнюю функцию, этот код все равно должен дождаться завершения someAsyncAction. Очень часто может потребоваться инициировать какое-либо асинхронное действие, но не ждать, пока обещание будет разрешено/ошибка, например, отправка аналитических данных. Теперь при объединении в цепочку .catch (и полном исключении await asyncResult) вы по-прежнему можете обрабатывать асинхронные ошибки в someAsyncAction, убедившись, что никакая другая логика не ожидает его. - person Edo; 25.10.2018