Hacker News new | past | comments | ask | show | jobs | submit login

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.




PSA: `setTimeout` can be `promisify`'d directly despite featuring flipped arguments, thanks to a built-in `util.promisify.custom` definition.


Promisify 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)
      ])
    )


Almost. 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.


Promises 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_origina...

[2]: https://github.com/nodejs/node/blob/ef16319effe396622666268c...


A 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.


Those 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).


But why. If you're gonna take special steps to handle `util.promisify` you might as well just offer a Promised API and skip `util.promisify`


The problem is that everyone does that slightly differently.

Having a stdlib function makes for an easy "let's just use this everywhere" answer.


What 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.


Does 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.


Looks really clean. Does bluebird handle this(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)]


Yes.

  .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.


Not 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).



The 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.


I have not tried, but I would guess no. Why not give it a try and see?


I 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.


If 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.



It's not entirely clear how to interpret that; can you provide a summary?


The 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.


Yes! I wrote about promisify a few weeks ago[0]

[0]: https://medium.com/@styfle/promises-in-node-js-8-x-core-d6a8...


I 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) );
  }


Takes a look at the code... logAppendResult almost same as logWriteResult...

Well, who are we to deprive you from the joy of writing lots of boilerplate code?


Have you ever tried to run more than one operation at once and collect the results?

(There are lots of other reasons to use the promise abstraction – having a type that can be transformed is extremely useful and natural – but that one’s pretty significant.)


    if (err) console.err('Failed to create file');
Congratulations, you're writing Go in JavaScript! :)


nah, this is valid javascript, Go came later, they might have copied a thing or 2 from C, like javascript did.


When callbacks are used with discipline, they are not much different from promises. The problem is when "discipline" part meets "human" part, though it's still true for promises, perhaps to a lesser degree.

The real value for promises is async/await.


Is 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.


No, 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.


Yes it doesn't handle the error case.

And if you replace the if(err) lines with a log(...) function it doesn't reduce them to one place. It makes you repeat the log(...) function everywhere. And you'd still need the if statement to handle the control flow.

Simple code is great, but not handling errors doesn't cut it for non throwaway applications.


You assume a lot.

We can refactor another round:

  function logFsResult( type, err ){
  	var msg= '';
  	switch ( type ) {
  		case 'append': msg= ( err ) ? 'Failed to update file' : 'Successfully updated file';
  			break;
  		case 'write': msg= ( err ) ? 'Failed to write file' : 'Successfully created file';
  			break;
  		default: msg= 'logFsResult error, missing or invalid first argument: '+ (type || '')
  	}
  	( err ) ? console.err( msg ) : console.log( msg );
  }

  function handleFile( filename, fileExists ) {
  	const timestamp = new Date().toISOString();
  	( fileExists )
  		?	fs.appendFile( filename, `Updated file on ${timestamp}\n`, logFsResult.bind(null, 'append') )
  		:	fs.writeFile( filename, `Created file on ${timestamp}\n`, logFsResult.bind(null, 'write') );
  }


Whenever you program in ECMAScript2016("Javascript"), you should take advantage of its features. Right now you're coding pretty much using the same way one would do it in C. Take advantage that functions are first-class in ES2016. Take advantage of JSON.

For example you could do something like this (sorry, don't have time to open my IDE to try the code i'm going to write):

    logFsResult = (type, err) => {
        map_action = {
          "append": { 
            True: "Succesfully updated file.",
            False: "Failed to update file."
          },
          "write": {
            True: "Successfuly created file.",
            False: "Failed to write file."
          }
        }

        message = map_action[type][(err != null)] // obtain the message
          method = (err)? console.err : console.log // choose logger
          method(err) //invoke the correct logger with the message

    }
This is an easier-to-mantain code. It separates the messages to use, from the logic. It allows you to easily configure/change the messages to log, and the method (function) for logging the error, without having to touch the decision logic. On your original example, the logic of the code was mingled with the error messages.

You could say this example was more "idiomatic" ES2016.


> Whenever you program in ECMAScript2016("Javascript"), you should take advantage of its features.

That's a trap, I should rather make my code as readable, scalable and bug free as possible regardless of ESxxx.

I can refactor in 10 other ways (different styles) coming to the same result, but that's not what my point was about. Using promises and so is just taste or preference, if you like it you use can it, if not do without. I've seen amazing spaghetti with promises and callbacks as well.

Easy to nitpick btw:

you compare err != null?? besides not using strict mode, what should err be? a String? So, what will happen if err is undefined or an empty String?

Then you call logFsResult with err while it is not used.. Did you even consider what happens if the value of type is not available in map_action? I'll be the end of the feast!

last one: True and False as an object key are not Booleans, so if you have your IDE up and running, the code will fail.

Now, you can try to solve this with promises, just as you can try brush your teeth with a broomstick.


> you compare err != null?? besides not using strict mode, what should err be? a String? So, what will happen if err is undefined or an empty String?

YOU are the one who made that comparison, not me. You originally wrote the following line:

   ( err ) ? console.err( msg ) : console.log( msg );
What do you think the "?" operator does with "err"?

> Then you call logFsResult with err while it is not used..

It seems you don't understand the code. I'm not calling "logFsResult", i am defining a function called logFsResult. You also did the same, you defined logFsResult to receive the "err" parameter.

  function logFsResult( type, err ){
  	var msg= '';
  	switch ( type ) {
> That's a trap, I should rather make my code as readable, scalable and bug free as possible regardless of ESxxx.

ES6 allows you to write more readable code than ES5. Take a look at the features.


That's still not error handling, that's error logging. Try writing code that depends on the success of the previous operation to perform another operation and you'll quickly find yourself in the callback soup.


Honestly that looks hideous.


You might like the make_esc/errify pattern [0]. You can apply the same function to any number of error callbacks in order to unify error handling. Works great with Iced CoffeeScript but also works well without. I can provide more examples but I think you'll get it.

[0]: https://github.com/nextorigin/el-borracho/blob/master/src/re...

P.S. Iced3 compiles to ES6 await so all of this has been working together for quite a while.


With Promises we conceptualize a control flow construct like callbacks. It is much easier to reason about a concept rather than following code execution paths. With async await we return control flow to the current scope.


One benefit comes from being able to use `async`/`await`.

Using `try`/`catch` with `async`/`await` is a bit awkward, though, which is especially unfortunate because it's the #1 place you should be handling errors.

LightScript has a language feature[0] that makes it less awkward (sort of an`Either`/`Result` type) that I'm thinking about submitting to TC39.

[0] – http://www.lightscript.org/docs/#safe-await


I will agree, to be honest I tried promises in my own projects and now writing them for someone. But to me Async.js is just cleaner nicer better. Funniest part is Bluebird docs on transitioning from it to Promises, promise example is bigger and messier.


Previously 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


I've been using https://github.com/sindresorhus/pify which is… also a different name but a similar one


Wow, what about good old promises? I totally dislike async, await, because they force you to switch to async code-flow-thinking.


>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.


I 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.


>The point is that node js is event driven and you can hardly escape from callbacks, except with syntatic sugar.

You can hardly escape from imperative code except without syntactic sugar either. That doesn't mean that one should program in the lower layer of abstraction of a platform. If we followed that, C programming would be all gotos instead of functions and the usual control flow (even "if" and "for" are syntactic sugar on top of assembly constructs).

>A sync await is an argument for people like you, who are trying hard to change its nature.

If it wasn't for people who tried hard to change its nature, JS would still be the same ho-hum language it was its first 15 years (I was there). Node.js was itself an attempt to "change" the nature of JS, moving it from client to server side.

The need for callbacks stems from the fact that JS as a language was never specifically event oriented -- any more than in any other language. It just supported DOM events in the browser environment, which for the first 10-15 years of the web were just simple one level callback handler (button clicked, do that). Hardly any kind of asynchronous programming to write home about. Aside from having first class functions, JS was not particularly designed for evented code. That's where callbacks came in, as a poor man's way to deal with evented code -- other languages have had coroutines, promises etc for 30+ years.


async / await works only with Promises. As the name util.promisfy indicates, it generates promises.

You never need use use async / await.


Thanks. 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.


Async and await do not make your code work synchronously, they make it look like synchronous.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: