Pitfalls of Promisifying by hand
When wrapping a traditional style callback naively into a Promise, you might end up with code that looks like this
deferredRead(), you would then use it
There exists 2 very serious shortcomings in this approach. Can you spot them?
Initiating function throws exception
If the initiating function throws exception, it will not propagate to the
fail()-handler. That is, before the deferred is properly set up if
fs.readFile() throws an error, the program will crash due to uncaught exception.
A better way would be to wrap the initiating function inside a try-catch block.
Result callback is called multiple times
The Promises/A+ spec states that a promise can only be fulfilled or rejected once.
A promise must provide a
thenmethod to access its current or eventual value or reason.
onFulfilledis a function, it must not be called more than once.
(Promises/A+ Section 2.2.2)
The wrapping function does not take this into account. It allows the callback to be called infinite number of times. A better way would be to wrap the callback function around a gatekeeper logic that allows it to be run only once and that would throw error otherwise.
On the other hand, ES6 spec states that if a promise is already resolved then further resolutions are just ignored. This would argue that in this case error should not be thrown. Implementations on this vary among promise libraries: kew does throw error but major ones like Bluebird, Q or native Promises do not.
The perfect Promises/A+ version
This is here for the sake of reference. However, you shouldn't try to write this beast every time you need a callback wrapped inside a promise. It's pretty error prone and you easily miss something. Better alternative is to use for example Bluebird.promisify() for this (see also promisifyAll() for doing this to a complete module at once).
The perfect ES6 native Promises version
If we leverage ES6 features we can write this more elegantly. The explicit try-catch can be eliminated since the constructor will call the reject handler in case an exception occurrs in the function. We can also remove the gatekeeper logic since if resolved more than once, the further resolves are just ignored.
But again, consider using ready made library for promisifying callbacks.
Node doesn't wait for your database call to finish?
Learn how asynchronous calls work and make your app run as you intended. Get short email course on asynchronicity and two chapters from Finish Your Node App.