Представим что мы разрабатываем библиотеку которая содержит функцию 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)
Несколько простых правил:
- старайтесь обрабатывать ошибки как можно "ближе" к возможному их источнику
- четко отделяйте в коде асинхронные операции от синхронных. В противном случае вы, скорее всего, закончите с очень "связанным" кодом.
ссылка на оригинал статьи https://habrahabr.ru/post/327560/
Добавить комментарий