No need to wrap everything in Promise
Imagine that we are developing a function isPi
:
function isPi(x) {
return x === 3.14
}
And then someone says: “We don’t want to know where the data comes from (database, network, etc.), so let's wrap our code in Promise”:
function isPi(px) {
return px.then(x =>
return x === 3.14
})
}
And we will call the function like this:
const px = grabXfromDb()
isPi(px)
- Cool, yeah? - Not
The problem is that the statement: “We wrap the code in Promise so that asynchronous or synchronous code is not steamed” is not true. We do not wrap the code, but Promise is "something" that can:
- either "resolve" with the value
- either "fail" with an error
sometime in the future:
px.then(x =>
// в этом месте x содержит уже известное "синхронное" значение
isPi(x)
)
So we can remove the redundant code:
function isPi(x) {
return x === 3.14
}
const px = grabXfromDb()
px.then(isPi)
You may ask: okay, the initial code is a little redundant, what's wrong with that? The problem arises when we try to add an error return - it is very easy to end with something like:
function isPi(px) {
return px.then(x =>
return x === 3.14
})
.catch(fuction(e) {
assert(e instanceof SomeDBError)
// какой-нибудь код на случай ошибки базы данных
})
}
This is bad because now our function knows through errors too much about the place where we got Promise. The correct way to rewrite this code is as follows:
function isPi(x) {
return x === 3.14
}
grabXfromDb()
.catch(fuction(e) {
assert(e instanceof SomeDBError)
// some code here
})
.then(isPi)
A few simple rules:
- try to handle errors as close as possible to their source
- clearly separate asynchronous operations from synchronous ones in the code. Otherwise, you will most likely end up with very “linked” code.