Вчера один опытный инженер показывал цепочку промисов:
// promise.then(onFulfilled: Function[, onRejected: Function]);
Promise.resolve("")
.then(a, b)
.then(c, d)
.then(e, f);
И спрашивал что будет, если a
падает (то бишь бросает):
Promise.resolve("")
.then(_ => { throw new Error("A") }, b)
.then(c, d)
.then(e, f);
У нас тут в джаваскрипте такие дела что, если исключение произошло в обработчике onFulfilled
, то оно проглатывается, и в рантайме ничего не падает. Это просто надо знать.
И спросил что будет дальше. А я и растерялся, потому что в промисах не алё и сам не понимаю как так вышло. Вроде и много раз видел, и пользовался даже, но сказал, что нормальная цепочка должена иметь .catch
в хвосте, куда и должны прилетать все исключения.
Promise.resolve("")
.then(_ => {throw new Error("💩")}, b)
.then(c, d)
.then(e, f)
.catch(val => {/* Error 💩 */});
Но обработка ошибки в одном месте — это недостаточно сложно, нет простора для ошибки, и откровенно скучно. Оказалось, что на самом деле ошибка придет в d
. Если пытаться продолжить эксперимент и бросить ошибку в d
то сюрприза не выйдет — упадет в рантайме.
Ну ладно, добавим в копилку еще один js wtf
, где исключение это то же самое, что и rejected promise
, но дальше то что? Ну я и говорю, что всё. Дальше некуда, навыполнялись. А промис взял и начал выполнять e
, принимая значение из d
. Еще раз, выполнять e
. После d
. Мы вызвали onFulfilled
с результатом из onRejected
. Что? Зачем?
Надо тебе в потоке данные модифицировать, ну возьми ты стрим. Надо в цепочке что-то посчитать — так есть же Either. Не надо жонглировать факелами, стоя на бритве.