11

In the book https://pragprog.com/book/tbajs/async-javascript, I found this:

Node’s early iterations used Promises in its nonblocking API. However, in February 2010, Ryan Dahl made the decision to switch to the now-familiar callback(err, results...) format, on the grounds that Promises are a higher-level construct that belongs in “userland.”

It looks quite confusing to me, because as an API to read files, this

fs.readFile('/etc/passwd')
.onSuccess(function(data){console.log(data)})
.onError(function(err){throw err})

looks much better than this:

fs.readFile('/etc/passwd', function (err, data) {
  if (err) throw err;
  console.log(data);
});

Does anyone have ideas about why "Promises are a higher-level construct" will stops itself from being used in NodeJS API?

Hanfei Sun
  • 45,281
  • 39
  • 129
  • 237
  • I don't know why the fs api doesn't use the promise, but you can use q-io/fs instead of the ordinary fs: https://github.com/kriskowal/q-io – Huy Hoang Pham May 18 '15 at 09:46
  • 4
    Would be nice to know the answer, but this is really a question that should belong to some Node.js mailing list rather than StackOverflow. – Simone May 18 '15 at 10:30
  • A callback is native, promises is a library. – Alexis Paques May 18 '15 at 10:48
  • @AlexisPaques Does that mean the "native callback" is not recommended to be used by application developers? – Hanfei Sun May 18 '15 at 11:07
  • Do you need a library to use a callback ?That's what I mean. It is lower lever programming – Alexis Paques May 18 '15 at 11:24
  • 4
    The promises used now in 2015 are a completely different thing than the "promises" that node used in 2010. In fact the equivalent code in 2015 promises wouldn't even have an error handler because error propagation is automatic instead of manual - you only need to have error handler when you actually handle an error – Esailija May 18 '15 at 11:53
  • Shouldn't a question on history *"Why was X was designed without Y"* be asked to programmers.se instead of SO ? – Denys Séguret May 18 '15 at 11:55
  • 2
    @AlexisPaques promises are a native language feature - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise just go to your dev console _anywhere_ and type `Promise` - it's there. The fact people use userland implementaitons which are superior should not be surprising - it's the same reason people use ImmutableJS over arrays or Ramda for functional manipulation instead of native language features or jQuery instead of the DOM - just because something is native doesn't make it always better. ATM userland implementations are better - that's a win for JS. – Benjamin Gruenbaum May 18 '15 at 11:59
  • Why not hear it directly from the horses mouth? The decision not to use promises is the first talking point in [10 Things I Regret About Node.js - Ryan Dahl - JSConf EU 2018](https://www.youtube.com/watch?v=M3BM9TB-8yA&feature=youtu.be). – Jake Holzinger Mar 16 '19 at 00:19

4 Answers4

19

Node v8 ships with util.promisify that converts callback APIs to promises, Node v10 ships with native promises support (experimental):

const fs = require('fs').promises;

// in an async function:
let data = await fs.readFile('/etc/passwd');
console.log(data);

The future is promises:

NodeJS will use promises for the new APIs. In fact it is currently discussed how. An earlier attempt in 0.2 to use Promises in node years ago failed because of friction and performance issues.

What has to happen first:

Now promises are a native language feature, but the following has to happen before they make it to the core APIs:

  • Promises have to be a native language construct this already happened.
  • The NodeJS and io.js merger that was recently announced has to happen - the time frame is a few short months probably.
  • The v8 (JavaScript engine) team has to finish working on private symbols which will enable fast promise creation. At the moment the promise constructor is the only way to create promises in native promises and it allocates a closure which is relatively expensive. This is currently being done with Domenic working in tight coordination between the io.js and v8 team to ensure this is done properly.
  • The v8 team has to optimize the promise implementation, currently native promises lose consistently to userland implementations like bluebird. This is also happening now.

Once all these happen the API will be forked and a version containing promises will be integrated into core. Here is a long and uninteresting discussion about it - there is a better one at the io.js/NG repo but neither are really too informative.

What can be done today

Libraries like give you tools to instantly convert a callback API to promises in a fast and efficient way. You can use them today and get that functionality.

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • v8 has private symbols already. They are used to implement "internal slots" for all kinds of objects including promises – vkurchatkin May 21 '15 at 23:53
  • @vkurchatkin can you find the relevant commit on v8 for me? – Benjamin Gruenbaum May 22 '15 at 05:43
  • Here is the code: https://github.com/v8/v8-git-mirror/blob/f2ffa6ade5e28d6ff1ed8895e320fc77bfb08c68/src/runtime/runtime-symbol.cc#L24 BTW there is a hacky way to use private symbols in userland: https://github.com/vkurchatkin/private-symbol – vkurchatkin May 22 '15 at 11:25
  • Any news on that? – Forivin Sep 06 '16 at 10:19
  • @BenjaminGruenbaum "NodeJS will use promises for the new APIs" => will they also provide it for the existing APIs? Also, what's your source? Is there any official statement by the node team on this, did you scramble it together through github threads, or did you have a private convo with the node guys? – bersling Sep 19 '17 at 10:22
  • @bersling I am a part of the Node core team and while decisions change and nothing is set in stone - there is current ongoing work about promisifying `fs` for starters - it's very tricky to get right without breaking anything - I encourage you to get involved if you care about the issue :) – Benjamin Gruenbaum Nov 15 '17 at 18:13
  • Which version of node will support the promise style ootb? Thanks. – Legends Mar 11 '18 at 16:10
  • @Legends the next one actually: v10. We're just waiting for the major version at this point, you can build Node yourself and start using tomorrow: https://github.com/nodejs/node/blob/master/doc/api/fs.md#fs-promises-api – Benjamin Gruenbaum Mar 11 '18 at 16:12
  • Aaaah cool, `node-nightly --upgrade`. Usually when doing `fs.readFile(path,"utf8").then((data)=>...` data will be string, when specifying the encoding, but with the latest version (`v10.0.0-nightly20180309099e621648`) I have to `data.toString("utf8")` again, to get the string. My first steps with `node` ;-) – Legends Mar 11 '18 at 17:36
  • 1
    @Legends please report all bugs and feature requests to https://github.com/nodejs/node - I promise that people will be inclusive and help you as well as fix things - feel free to ping me when you've posted an issue and I'll make sure of that. (Questions are fine in nodejs/help ). – Benjamin Gruenbaum Mar 11 '18 at 17:38
  • This doesn't work! `Error: Cannot find module 'fs/promises'`. It doesn't exist on npm either so I can't install it. What the hell man? – Peter Weyand Mar 14 '19 at 21:04
5

Historically callbacks are the default for performance reasons, but...

Update 2017 / Node 8: Promises are now supported by the core!

Node.js supports promises since Node v8.x. The APIs are all still written in callback style (for backwards compatibility etc.), but there now is a utility class in node core to convert the callback-based APIs to promise-based APIs (similarly to bluebird):

https://nodejs.org/api/util.html#util_util_promisify_original

From the Node.js docs:

For example:

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);
stat('.').then((stats) => {
  // Do something with `stats`
}).catch((error) => {
  // Handle the error.
});

Or, equivalently using async functions:

const util = require('util');
const fs = require('fs');

const stat = util.promisify(fs.stat);

async function callStat() {
  const stats = await stat('.');
  console.log(`This directory is owned by ${stats.uid}`);
}
bersling
  • 17,851
  • 9
  • 60
  • 74
  • Unbelievable, that one has to wrap it ... this should be ootb when using async API .. oooh my... – Legends Mar 11 '18 at 16:06
4

Update 2018 / Node 10: New fs.promises API

The fs.promises API provides an alternative set of asynchronous file system methods that return Promise objects rather than using callbacks. The API is accessible via require('fs').promises.

https://nodejs.org/api/fs.html#fs_fs_promises_api

(experimental at this moment, but working perfectly on node latest)

Léonard.
  • 64
  • 5
  • We're actually still changing it a lot, we just switched from `fs/promises` to `fs.promises` and removed half the API (all the static methods). I recommend people consider that this is experimental and emits a warning when used before using it in production apps. We would love feedback though! – Benjamin Gruenbaum May 23 '18 at 20:02
  • I get a warning on my app. `ExperimentalWarning: The fs.promises API is experimental`. Very helpful. Except - what triggers that warning? How do I track down which module is trying to use this experimental thing? How do I make it stop? – Cheeso Jun 27 '18 at 00:52
-4

Promises is a library, when using promise it requires to return Promise constructor from function but using callback function chaining same thing is achievable that's why "Promises are a higher-level construct"

Reference: Promises in node js

webpandit
  • 47
  • 4