Представим что мы разрабатываем функцию isPi
:
function isPi(x) {
return x === 3.14
}
И тут кто-то говорит:?"Мы не хотим знать откуда приходят данные (БД, сеть и т.д.), по-этому давайте обернем наш код в Promise":
function isPi(px) {
return px.then(x =>
return x === 3.14
})
}
И будем вызывать функцию так:
const px = grabXfromDb()
isPi(px)
– круто, да? – Нет
Проблема в том, что утверждение: "Мы оборачиваем код в Promise, чтобы не парится асинхронный код или синхронный" – не верно. Мы не оборачиаем код, но Promise это "что-то", что может:
- либо "разрешиться" с значением
- либо "сорваться" с ошибкой
когда-нибудь в будущем:
px.then(x =>
// в этом месте x содержит уже известное "синхронное" значение
isPi(x)
)
А значит мы можем удалить избыточный код:
function isPi(x) {
return x === 3.14
}
const px = grabXfromDb()
px.then(isPi)
Вы можете спросить: ну ладно, начальный код чуть избыточен, что в этом плохого? Проблема возникает когда мы пытаемся добавить обратку ошибок – очень легко закончить с чем-то вроде:
function isPi(px) {
return px.then(x =>
return x === 3.14
})
.catch(fuction(e) {
assert(e instanceof SomeDBError)
// какой-нибудь код на случай ошибки базы данных
})
}
Это плохо потому что теперь наша функция через ошибки знает слишком много о месте откуда мы взяли Promise. Правильный способ переписать этот код выглядит так:
function isPi(x) {
return x === 3.14
}
grabXfromDb()
.catch(fuction(e) {
assert(e instanceof SomeDBError)
// some code here
})
.then(isPi)
Несколько простых правил:
- старайтесь обрабатывать ошибки как можно "ближе" к возможному их источнику
- четко отделяйте в коде асинхронные операции от синхронных. В противном случае вы, скорее всего, закончите с очень "связанным" кодом.
Комментарии (7)
rumkin
28.04.2017 14:44+2М… Даже странно как-то видеть такую статью. Промис в принципе не должен передаваться как аргумент, если это не функция для работы с промисами, такая как
Promise.all
илиPromise.race
. Так же как и скрываться ошибка из промиса не должна внутри библиотечной функции.HaMI
28.04.2017 15:19+1Аминь. Но, к сожалению, это не для всех очевидно – статья это попытка «доказать» ваше утверждение
PaulMaly
29.04.2017 11:52+1Никогда не видел, чтобы кто-то так писал. Возвращать промисс это да, но передавать параметром как-то не принято. Может быть мне везло.
Apx
30.04.2017 12:52Ну иногда видел похожие вещи в коде. Зачастую такой код любят писать c promise.All Читать может и красиво а вот с реверс инженирингом начинается батхерт. Если помнить одно простое правило что промис чейнится можно спокойно писать обычные функции и вызывать цепочку. И вот такой огород не нужен.
Единственное когда я таким вот сам лично грешу это когда пишу стабы для апи которого ещё нет. Так сразу видно другим людям что тут когда-то будет апи вызов.
napa3um
Преждевременная оптимизация — зло. Но её путают часто со следованием архитектуре (как бы подразумеваемой, неявно, но которой зачастую попросту нет в момент принятия таких решений).
Решать, какие функции и методы должны быть синхронными, какие асинхронными, какие будут возвращаться ошибки и как обрабатываться, какие типы модулей могут использовать какие модули, каким модулям можно ходить в интернет, а каким в БД и всё такое прочее нужно на этапе проектирования архитектуры, а не гадая по одной конкретной функции, кто её в будущем и как решит использовать.
HaMI
Простите, но у меня сложилось впечатление что вы не читали статью и не понимаете значение слова «пример».