{"action":"created","issue":{"url":"https://api.github.com/repos/nodejs/node/issues/5020","repository_url":"https://api.github.com/repos/nodejs/node","labels_url":"https://api.github.com/repos/nodejs/node/issues/5020/labels{/name}","comments_url":"https://api.github.com/repos/nodejs/node/issues/5020/comments","events_url":"https://api.github.com/repos/nodejs/node/issues/5020/events","html_url":"https://github.com/nodejs/node/pull/5020","id":130309320,"number":5020,"title":"Adding Core support for Promises","user":{"login":"chrisdickinson","id":37303,"avatar_url":"https://avatars.githubusercontent.com/u/37303?v=3","gravatar_id":"","url":"https://api.github.com/users/chrisdickinson","html_url":"https://github.com/chrisdickinson","followers_url":"https://api.github.com/users/chrisdickinson/followers","following_url":"https://api.github.com/users/chrisdickinson/following{/other_user}","gists_url":"https://api.github.com/users/chrisdickinson/gists{/gist_id}","starred_url":"https://api.github.com/users/chrisdickinson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrisdickinson/subscriptions","organizations_url":"https://api.github.com/users/chrisdickinson/orgs","repos_url":"https://api.github.com/users/chrisdickinson/repos","events_url":"https://api.github.com/users/chrisdickinson/events{/privacy}","received_events_url":"https://api.github.com/users/chrisdickinson/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/nodejs/node/labels/semver-major","name":"semver-major","color":"fbca04"}],"state":"open","locked":false,"assignee":{"login":"chrisdickinson","id":37303,"avatar_url":"https://avatars.githubusercontent.com/u/37303?v=3","gravatar_id":"","url":"https://api.github.com/users/chrisdickinson","html_url":"https://github.com/chrisdickinson","followers_url":"https://api.github.com/users/chrisdickinson/followers","following_url":"https://api.github.com/users/chrisdickinson/following{/other_user}","gists_url":"https://api.github.com/users/chrisdickinson/gists{/gist_id}","starred_url":"https://api.github.com/users/chrisdickinson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chrisdickinson/subscriptions","organizations_url":"https://api.github.com/users/chrisdickinson/orgs","repos_url":"https://api.github.com/users/chrisdickinson/repos","events_url":"https://api.github.com/users/chrisdickinson/events{/privacy}","received_events_url":"https://api.github.com/users/chrisdickinson/received_events","type":"User","site_admin":false},"milestone":null,"comments":222,"created_at":"2016-02-01T09:35:41Z","updated_at":"2016-02-02T18:08:00Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/nodejs/node/pulls/5020","html_url":"https://github.com/nodejs/node/pull/5020","diff_url":"https://github.com/nodejs/node/pull/5020.diff","patch_url":"https://github.com/nodejs/node/pull/5020.patch"},"body":"**Update 02/01/16 14:10:00 PST**: [Current state of the PR.](https://github.com/nodejs/node/pull/5020#issuecomment-178223110)\r\n\r\nThis PR introduces Core support for Promises. Where possible, callbacks have\r\nbeen made optional; when omitted, a `Promise` is returned. For some APIs (like\r\n`http.get` and `crypto.randomBytes`) this approach is not feasible. In those\r\ncases promise-returning `*Async` versions have been added\r\n(`crypto.randomBytesAsync`). The naming there isn't set in stone, but it's\r\nbased on how [`bluebird`][ref-bluebird]'s [`promisifyAll`][ref-promisify-all]\r\nmethod generates names.\r\n\r\nThis PR allows users to swap the *implementation* of promises used by core.\r\nThis helps in a few dimensions:\r\n\r\n* Folks that prefer a particular flavor of Promises for sugar methods can set\r\n  the promise implementation at app-level and forget about it.\r\n* This should alleviate concerns about stifling ecosystem creativity — putting\r\n  promises in Core doesn't mean Core needs to pick a winner.\r\n* Folks who prefer a faster implementation of promises can swap in their\r\n  preferred library; folks who want to use whatever native debugging is added\r\n  at the V8 level for native promises can avoid swapping in promises.\r\n* A world of benchmarks opens up for Promise library authors — any code in the\r\n  ecosystem that uses promises generated by core and has a benchmarking suite\r\n  is a potential benchmark for promise libraries.\r\n\r\nThe API for swapping implementations is `process.setPromiseImplementation()`.\r\nIt may be used many times, **however** after the first time deprecation warnings\r\nare logged with the origin of the *first* call. This way if a package misbehaves\r\nand sets the implementation for an application, it's easy to track down.\r\n\r\nExample:\r\n\r\n```js\r\nprocess.setPromiseImplementation(require('bluebird'));\r\n\r\nconst fs = require('fs');\r\n\r\nfs.readFile('/usr/share/dict/words', 'utf8')\r\n  .then(words => console.log(`there are ${words.split('\\n').length} words`))\r\n  .catch(err => console.error(`there are no words`));\r\n```\r\n\r\nStreams are notably missing from this PR – promisifying streams is more\r\ninvolved since the ecosystem already relies on a common (and more importantly,\r\noften **pinned**) definition of how that type works. Changing streams will take\r\neffort and attention from the @nodejs/streams WG. All other callback-exposing\r\nmodules and methods have been promisified (or an alternative made available),\r\nthough.\r\n\r\nThree new internal modules have been added:\r\n\r\n* `lib/internal/promises` — tracks the original builtin `Promise` object, as \r\n  well as the currently selected `Promise` implementation. All promises \r\n  created by Node are initially native, and then passed to the selected\r\n  implementation's `.resolve` function to cast them.\r\n* `lib/internal/promisify` — turn a callback-accepting function into one that\r\n  accepts callbacks *or* generates promises. Errors thrown on the same tick\r\n  are still thrown, not returned as rejected promises — in other words,\r\n  programmer errors, such as invalid encodings, are still eagerly thrown.\r\n* `lib/internal/callbackify` — turn a synchronous or promise-returning function\r\n  into a callback-accepting function. This is only used in `lib/readline` for\r\n  the `completer` functionality. This could probably fall off this PR, but it\r\n  would be useful for subsequent changes to streams.\r\n\r\n## Breaking Changes\r\n\r\n* In general: any API that would have thrown with no callback most likely does\r\n  **not** throw now.\r\n* `fs` APIs called without a callback will no longer crash the process with an\r\n  exception.\r\n* `ChildProcess#send` with no callback no longer returns `Boolean`, instead\r\n  returns `Promise` that resolves when `.send` has completed.\r\n* Wrapped APIs:\r\n  * `child_process.ChildProcess#send`\r\n  * `cluster.disconnect`\r\n  * `dgram.Socket#bind`\r\n  * `dgram.Socket#close`\r\n  * `dgram.Socket#send`\r\n  * `dgram.Socket#sendto`\r\n  * `dns.lookupService`\r\n  * `dns.lookup`\r\n  * `dns.resolve`\r\n  * `fs.access`\r\n  * `fs.appendFile`\r\n  * `fs.chmod`\r\n  * `fs.chown`\r\n  * `fs.close`\r\n  * `fs.exists`\r\n  * `fs.fchmod`\r\n  * `fs.fchown`\r\n  * `fs.fdatasync`\r\n  * `fs.fstat`\r\n  * `fs.fsync`\r\n  * `fs.ftruncate`\r\n  * `fs.futimes`\r\n  * `fs.lchmod`\r\n  * `fs.lchown`\r\n  * `fs.link`\r\n  * `fs.lstat`\r\n  * `fs.mkdir`\r\n  * `fs.open`\r\n  * `fs.readFile`\r\n  * `fs.readdir`\r\n  * `fs.readlink`\r\n  * `fs.realpath`\r\n  * `fs.rename`\r\n  * `fs.rmdir`\r\n  * `fs.stat`\r\n  * `fs.symlink`\r\n  * `fs.unlink`\r\n  * `fs.utimes`\r\n  * `fs.writeFile`\r\n  * `net.Socket#setTimeout`\r\n  * `readline.Interface#question`\r\n  * `repl.REPLServer#complete`\r\n  * `zlib.deflateRaw`\r\n  * `zlib.deflate`\r\n  * `zlib.gunzip`\r\n  * `zlib.gzip`\r\n  * `zlib.inflateRaw`\r\n  * `zlib.inflate`\r\n  * `zlib.unzip`\r\n\r\n## New APIs\r\n\r\n* **`process.setPromiseImplementation`**\r\n* `net.connectAsync`\r\n* `tls.connectAsync`\r\n* `crypto.randomBytesAsync`\r\n* `crypto.pbkdf2Async`\r\n* `crypto.pseudoRandomBytesAsync`\r\n* `crypto.rngAsync`\r\n* `crypto.prngAsync`\r\n* `http.getAsync`\r\n* `https.getAsync`\r\n* `http.requestAsync`\r\n* `https.requestAsync`\r\n* `child_process.execAsync`\r\n* `child_process.execFileAsync`\r\n\r\n## Next steps\r\n\r\nI haven't finished this PR yet: the primary missing piece is tests for the\r\npromise-based APIs, to ensure that they resolve to the correct values. Docs\r\nalso need updated. I'll be working on this in my free time over the next week.\r\n\r\nHere are the bits that I'd like folks reading this to keep in mind:\r\n\r\n* The code changes here are fairly cheap, time-wise — one evening's worth of work.\r\n  * Technical possibility has never been the primary blocker.\r\n* We know async/await is coming down the pipe, and many devs are interested in\r\n  it.\r\n  * With ChakraCore potentially coming in, this may happen sooner than anyone\r\n    previously imagined.\r\n  * Even sans the ChakraCore PR, it's my understanding that V8 will be supporting\r\n    async/await by EOY (Correct me if I'm wrong, here!)\r\n* **Promises may not be your cup of tea.** This is okay. This is not an attempt to \r\n  **replace** callbacks or streams with promises. They can co-exist. While Promises \r\n  are complicated, much of that complication falls out of the problem space that \r\n  both callbacks and promises abstract over. Give this a fair shake.\r\n\r\n[ref-bluebird]: http://bluebirdjs.com/\r\n[ref-promisify-all]: http://bluebirdjs.com/docs/api/promise.promisifyall.html"},"comment":{"url":"https://api.github.com/repos/nodejs/node/issues/comments/178727217","html_url":"https://github.com/nodejs/node/pull/5020#issuecomment-178727217","issue_url":"https://api.github.com/repos/nodejs/node/issues/5020","id":178727217,"user":{"login":"mikeal","id":579,"avatar_url":"https://avatars.githubusercontent.com/u/579?v=3","gravatar_id":"","url":"https://api.github.com/users/mikeal","html_url":"https://github.com/mikeal","followers_url":"https://api.github.com/users/mikeal/followers","following_url":"https://api.github.com/users/mikeal/following{/other_user}","gists_url":"https://api.github.com/users/mikeal/gists{/gist_id}","starred_url":"https://api.github.com/users/mikeal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mikeal/subscriptions","organizations_url":"https://api.github.com/users/mikeal/orgs","repos_url":"https://api.github.com/users/mikeal/repos","events_url":"https://api.github.com/users/mikeal/events{/privacy}","received_events_url":"https://api.github.com/users/mikeal/received_events","type":"User","site_admin":false},"created_at":"2016-02-02T18:08:00Z","updated_at":"2016-02-02T18:08:00Z","body":"@rvagg @domenic \r\n\r\n> A given project may end up containing all of these [API wrapping] libraries in the same project. I'd much prefer to just have core supply a single implementation that everyone uses, especially for something as basic as fs.\r\n\r\nThis problem is dramatically worse for users of js compilers. It means that several versions of several wrapping libraries get included in the bundle for front-end assets that all essentially do the same thing. Progress has been made in npm to flatten dep trees and help people to remove duplicate versions of the same library but having several competing libraries for simple wrapping of core APIs can't be de-duplicated without maintaining forks of all the libraries that use them.\r\n\r\nI think the biggest argument for this PR is this: when Promises landed natively in V8, and soon after in Node.js, we were able to make some serious improvements to Node.js as a platform for Promise users. Not simply because of the fact that they were supported \"natively\" but because the Node.js platform was able to add features specifically for native Promises that weren't available to user-land Promises.\r\n\r\nLooking at the current breaks in domains and our lack of AsyncWrap, I see these as reasons *to* add these APIs, not as arguments against. Landing broader support for Promises *should* increase the opportunity for Promise users to solve these problems in Node.js and land patches for support. It should increase the drive of Promise users to become contributors to Node.js to work on these issues.\r\n\r\nI'm not of the mind that the moment these land the ecosystem adopts them wholeheartedly and in the future we stop seeing callbacks, I find that view of the future somewhat delusional. But I do think a future where Promise users are engaged in the platform *more* is likely a good thing for Node.js and that this isn't a zero sum game between people who like Promises and people who prefer callbacks."}}