Skip to content
This repository was archived by the owner on Mar 31, 2018. It is now read-only.
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Create conversion.md
  • Loading branch information
benjamingr committed Feb 16, 2016
commit 71ad19f71e1da9730ed6ed2e36ff9e1a82cc0b68
119 changes: 119 additions & 0 deletions patterns/conversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Working with callbacks

This page explains how promises work with APIs that return callbacks.

When users have a callback API in a format like:

###1. One time event:

```js
process.on("rejectionHandled", function(e) { // I only ever want to deal with this once

});
```

###2. Plain non-standard callback:

```js
function request(onChangeHandler){
...
request(function(){
// change happened
});
```

###3. Standard style callback ("nodeback"):

```js
function getStuff(dat,callback){
...
getStuff("dataParam",function(err,data){

}
```
###4. A whole library with node style callbacks:

```js
API;
API.one(function(err,data){
API.two(function(err,data2){
API.three(function(err,data3){

})
});
});
```

###How do I work with the API in promises, how do I "promisify" it?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might need a space between # and H here.


First of all, there are plans to add support to promises in core. You can track
that here: https://github.com/nodejs/node/pull/5020

APIs should only be converted to return promises if they do not already return promises.

Promises have state, they start as pending and can settle to:

- __fulfilled__ meaning that the computation completed successfully.
- __rejected__ meaning that the computation failed.

Promise returning functions _should never throw_, they should return rejections instead.
Throwing from a promise returning function will force you to use both a `} catch { ` _and_ a `.catch`.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example here would be very helpful.

People using promisified APIs do not expect promises to throw. If you're not sure how async
APIs work in JS - please [see this answer](http://stackoverflow.com/questions/14220321) first.

###1. DOM load or other one time event:

So, creating promises generally means specifying when they settle - that means when they move
to the fulfilled or rejected phase to indicate the data is available (and can be accessed with `.then`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might reword this — may I suggest:

Creating a promise directly usually means choosing the point at which it should settle.

Once we land the glossary we can link settle to the appropriate glossary term.


```js
function load() { // our event
return new Promise(function(resolve,reject) { // create a new promise
process.on("rejectionHandled", resolve); // resolve it when the event happens
});
}
```

###2. Non-Standard callback:

These APIs are rather common since well... callbacks are common in JS. In Node it is strictly prefered

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preferred

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest omitting strictly, since Node itself breaks from this convention at times (fs.exists).

that callback code you write uses the standard `(err, data)` convention.

Let's look at the common case of having `onSuccess` and `onFail`:

function getUserData(userId, onLoad, onFail){ ...

This would look like the following

```js
function getUserDataAsync(userId) {
return new Promise(function(resolve,reject) {
getUserData(userId, resolve, reject); // pass reject/resolve handlers explicitly
});
}
```

###3. Standard style callback ("nodeback"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should define nodeback in the glossary and prefer that term's use when describing the err-first callback pattern.


Node style callbacks (nodebacks) have a particular format where the callbacks is always the last
argument and its first parameter is an error. Let's first promisify one manually:

getStuff("dataParam",function(err,data){

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code highlight is missing


To:

```js
function getStuffAsync(param) { // a function that takes the parameter without the callback
return new Promise(function(resolve,reject) { // we invoke the promise constructor
getStuff(param,function(err,data) { // call the function internally
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this try/catch the fn as well, since it's common for functions taking a nodeback to throw if input params are badly shaped?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Promise already catches all errors and rejects the promise.

if(err) return reject(err); // if it errord we reject
resolve(data); // otherwise re resolve

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we

});
});
}
```
Notes:

- Of course, when you are in a `.then` handler you do not need to promisify things.
Returning a promise from a `.then` handler will resolve or reject with that promise's value.
Throwing from a `.then` handler is also good practice and will reject the promise.