6 ms·
For me, this one brought happiness: Node.js 8.0.0 includes a new util.promisify() API that allows standard Node.js callback style APIs to be wrapped in a funct
by flavio81 9y ago
For me, this one brought happiness:
Node.js 8.0.0 includes a new util.promisify() API that allows standard Node.js callback style APIs to be wrapped in a function that returns a Promise. An example use of util.promisify() is shown below.
This is great stuff. This enables writing code using async and await at all times, which is what any sane developer would do when writing code for Node.js.
- AngelloPozo 9y agoWhat is nice is that it eats into bluebird. I love bluebird, and there are some nice utility functions. But if you only used it to promisify or create promises, there may be no need to keep it.
- Androider 9y agoDoes Node support filtered errors like Bluebird? somePromise.then(function() { return a.b.c.d(); }).catch(TypeError, ReferenceError, function(e) { //Will end up here on programmer error }).catch(NetworkError, TimeoutError, function(e) { //Will end up here on expected everyday network errors }).catch(function(e) { //Catch any unexpected errors }); It's super useful.
- AngelloPozo 9y agoI have not tried, but I would guess no. Why not give it a try and see?
- kkennis 9y agoThe standard way to handle errors coming from 'await' is try/catch, and any errors can be handled in the catch block as if they were coming from a synchronous context. So, you'd now filter async errors the same way you filter synchronous ones.
- Tarean 9y agoLooks really clean. Does bluebird handle this(https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Exception.html#v:catches https://hackage.haskell.org/package/base-4.9.0.0/docs/Contro...) problem? Sometimes you want to catch two different sorts of exception. You could do something like f = expr `catch` \ (ex :: ArithException) -> handleArith ex `catch` \ (ex :: IOException) -> handleIO ex However, there are a couple of problems with this approach. The first is that having two exception handlers is inefficient. However, the more serious issue is that the second exception handler will catch exceptions in the first, e.g. in the example above, if handleArith throws an IOException then the second exception handler will catch it. Instead, we provide a function catches, which would be used thus: f = expr `catches` [Handler (\ex :: ArithException -> handleArith ex), Handler (\ex :: IOException -> handleIO ex)]
- idbehold 9y agoYes. .catch(ArithException, IOException, handleError) Edit: Nevermind, I think what you want to be able to do is provide two different error handlers, but essentially catch them at the same time so that if you throw inside one of them, the second one wouldn't catch it.
- NoInkling 9y agoNot related to the aforementioned Bluebird feature, but I think that's the very reason the promise spec allows you to specify an error callback as the second argument to .then. I guess you can always fallback to an if/switch statement if it's a concern (which is what you'd do with await and try/catch).
- tkxxx7 9y agothis library does: https://github.com/bigeasy/cadence https://github.com/bigeasy/cadence
- drinchev 9y agoI constantly use `.map` and `.reduce` out of bluebird. I'm not sure I will replace these soon, since it's out of the Promise A+ specs. As a matter of fact, does anyone have a benchmark of the new nodejs 8's promise implementation against bluebird, because I so far bluebird was faster than the native implementation.
- l1ambda 9y agoJust ran it: https://gist.github.com/llambda/aa44d2d44b8356e02f12493ee6e6d27e https://gist.github.com/llambda/aa44d2d44b8356e02f12493ee6e6...
- rattray 9y agoIt's not entirely clear how to interpret that; can you provide a summary?
- spion 9y agoThe short story is that native promises are faster now except for the "promisification" part. The benchmark was designed for realistic use in a node environment, where most of the libraries come callback based. Because of that a very fast "promisify" is really important. Native promises don't provide one so the naive implementation using standards-compatible API is quite slow. Bluebird's promisify is a lot faster since it relies on non-standard (as in non-ES6-standard) internals instead of using the promise constructor as an ES6-based promisifier would need to do. edit: on second thought, I haven't looked at the included `util.promisify` - it could be taking advantage of non-public internal V8 promise APIs.
- franciscop 9y agoIf I remember correctly there is a 4.5x speed up in the bundled V8 (chrome engine) implementation, making it on-par speed-wise with bluebird, however that is hardly your bottleneck anyway.
- styfle 9y agoYes! I wrote about promisify a few weeks ago[0] [0]: https://medium.com/@styfle/promises-in-node-js-8-x-core-d6a8a93e85a2 https://medium.com/@styfle/promises-in-node-js-8-x-core-d6a8...
- twii 9y agoI still don't get the need for Promises.. Almost all examples, just like this one in the provided link, talk about solving callback hell with Promises, while callback hell is just a bad way of writing software imao. Look at the code below and please tell me why your example with Promises is a better solution. function logAppendResult( err ) { if (err) console.err('Failed to update file'); else console.log('Successfully updated file'); } function logWriteResult( err ) { if (err) console.err('Failed to create file'); else console.log('Successfully created file'); } function handleFile( filename, fileExists ) { const timestamp = new Date().toISOString(); ( fileExists ) ? fs.appendFile( filename, `Updated file on ${timestamp}\n`, logAppendResult ) : fs.writeFile( filename, `Created file on ${timestamp}\n`, logWriteResult ); } function main() { const filename = './example.txt'; exists( filename, (fileExists) => handleFile(filename, fileExists) ); }
- koolba 9y agoIs this a joke? You've repeated the if(err) check in three places. None of your error handling bubbles up so the handlers (i.e. Logging to console.error) are buried in the individual functions. You've pretended to avoid nested callbacks by inlining them using arrow functions. For your sake I hope that was sarcastic.
- twii 9y agoNo, it's not a joke. A junior dev can read this code and it will be very hard to create bugs. Inlining with arrows is just a more functional approach, as I would write in Coffee: exists filename, (fileExists) -> handleFile filename, fileExists Anything wrong with that line of code? I just try very hard to keep my code as simple as possible. I hope you know it's trivial to write a log function that accepts different callees so you end up with only 1 'if (err)'. I didn't test, but I wouldn't be surprised if my example runs times faster as well btw.
- evv 9y agoPreviously I had used a module called "denodeify" that achieves the same thing as promisify. I can see why the node maintainers used a different name :-D
- floatboth 9y agoI've been using https://github.com/sindresorhus/pify https://github.com/sindresorhus/pify which is… also a different name but a similar one
- drinchev 9y agoWow, what about good old promises? I totally dislike async, await, because they force you to switch to async code-flow-thinking.
- flai 9y agoasync / await works only with Promises. As the name util.promisfy indicates, it generates promises. You never need use use async / await.
- drinchev 9y agoThanks. I actually wrote the comment, because of : > This enables writing code using async and await at all times, which is what any sane developer would do when writing code for Node.js.
- egeozcan 9y agoAsync and await do not make your code work synchronously, they make it look like synchronous.
- coldtea 9y ago>I totally dislike async, await, because they force you to switch to async code-flow-thinking. If you started programming with Node, it's like you've learned doing everything the wrong way. The async/await brings back the sane, synchronous, reasoning. There's a reason that from Lisp to Haskell, every language tried to get rid of the error-prone callback spaghetti.
- drinchev 9y agoI have not started programming wit JavaScript. The point is that node js is event driven and you can hardly escape from callbacks, except with syntatic sugar. A sync await is an argument for people like you, who are trying hard to change its nature. It was added to the specs, because of "browsers got stuck to JavaScript". There are better server side languages, so you can use them.
- idbehold 9y agoPromisify is a super simple function to write yourself though. const promisify = (fn, ctx) => (...args) => new Promise((resolve, reject) => fn.apply(ctx, [ ...args, (err, data) => err ? reject(err) : resolve(data) ]) )
- elmigranto 9y agoAlmost. Don't forget about the case where callback receives multiple arguments, and the fact that someone might decide to change the API for callback signature like `(err, result)` to `(err, result, stats)`, for example.
- idbehold 9y agoPromises only support a single success value so if you're "promisifying" something then you're only going to the second argument as the resolved value. The new util.promisify() doesn't provide said functionality [1] will only resolve the second argument [2] unless you define a custom function on the original function. [1]: https://nodejs.org/api/util.html#util_util_promisify_original https://nodejs.org/api/util.html#util_util_promisify_origina... [2]: https://github.com/nodejs/node/blob/ef16319effe396622666268cd8100ad8d586c4ee/lib/internal/util.js#L204-L245 https://github.com/nodejs/node/blob/ef16319effe396622666268c...
- elmigranto 9y agoA bunch of functions in core return multiple things to callback. A lot more in userland do the same. Spec or not, that is still something that needs to be accounted for.
- idbehold 9y agoThose are handled by `util.promisify()` by having the original function define special properties on itself `Symbol('util.promisify.custom')` and `Symbol('customPromisifyArgs')`. But it's not something handled by default (e.g. resolving an array if the callback gets more than 2 arguments passed to it).
- smilekzs 9y agoPSA: `setTimeout` can be `promisify`'d directly despite featuring flipped arguments, thanks to a built-in `util.promisify.custom` definition.