I Promised to make this presentation. 

I finally did it.

 

 

 

Note: All syntax is based off the Bluebird Promise library.

or for the full spec

https://github.com/promises-aplus/promises-spec

 

What are Promises?

 

promise represents the eventual result of an asynchronous operation. 

 

 

https://github.com/promises-aplus/promises-spec

Wat?

It turns this...

into this

function retweetAllThe(hashtag, cb) {
    twitter.hashtagged(hashtag, function(err, allTheThings) {
        if (err) { return cb(err); }
        else {
            twitter.reTweet(allTheThings, function(err, numberOfRetweets) {
                if (err) { return cb(err); }
                else {
                    return cb(undefined, numberOfRetweets);
                }
            });
        }
    });
};
retweetAllThe('cats', function(err, numberOfRetweets) {
    if (err) { console.log('Where have all the cats gone?'); }
    else { console.log(numberOfTweets); }
});
twitter.hashtagged(hashtag)
.then(twitter.reTweet)
.then(console.log)
.catch(function(err) {
    console.log('Where have all the cats gone?');
});

Promises releases you from the shackles of callback hell.

By wrapping each function into something that returns a then. 

*But you should really understand Callbacks first. Knowing how to write a piece of code as a callback means that it'll work as a promise as well.

A Then, or thennable, as some people call it, is any resulting object that has a .then property. 

The .then property allows other promises/functions to chain off the original promise by inserting another function as the parameter to the .then().

Hokay, So.

How do I start a Promise?

Well, you probably won't be starting any promises... probably.

Most libraries are starting to support Promises or can be modified to support them. 

Okay, I lied. 

Sometimes you need to start your own Promise.

To start a promise...

Text

function someFunction(params) {
    return new Promise(function(resolve, reject) {
        // do something with params

        // end with calling resolve(whatYouWantToReturn); 
        // or with reject(err);
    });
}

someFunction('something')
.then(function(result) {
    // do something with the results
})
.catch(function(err) {
    // all your errors are belong to this function
});

What if I want to do thing concurrently?

function spark(power) {
    return new Promise(function(resolve, reject) {
        power.connect();
        return resolve('sparked');
    });
}

function pump(fuel) {
    return new Promise(function(resolve, reject) {
        fuel.flow();
        return resolve('pumping');
    });
}

var fuelPump = pump(gas);
var starter = spark(electricity);
return Promise.join(starter, fuelPump)
.then(function(actions) {
    // ['sparked', 'pumping']
    if (actions.length === 2) { console.log('Engine started'); }
});
.catch(function(err) {
    console.log('boom.');
});

So why should I use Promises?

It's the future of JavaScript. 

 

ES6 will come with Promises natively.

Because callbacks kinda do suck.

Also, it's fast.

It's about 14% slower compared to native unflattened callbacks but that shouldn't matter unless you do about 10000 ops/sec @ 1ms per I/O op.

 

For comparison, async-waterfall is about 300% slower.

 

http://spion.github.io/posts/why-i-am-switching-to-promises.html

The real reason to use promises...

Text

// Positive case only
someFunction('something')
.then(function(result) {
  // do something with the results\
  throw new Error('OH NOEESSSS!');  
})
.catch(function(err) {
    // all your errors are belong to this function
    console.log(err); // OH YES!
});


// Callback form (must always all cases)
someFunction(param, cb) { // cb = anotherFunction
  // need to ensure param is valid
  return cb(err, result); // need to pass errors down the road here
}

anotherFunction(err, value, cb) {
  // need to handle err here or pass it on
  // do something with value
  // pass error or generate a new on
  return cb(err, value);
}

So I can't use it now?

Promise Libraries

https://github.com/kriskowal/q

https://github.com/petkaantonov/bluebird

https://github.com/tildeio/rsvp.js

https://github.com/cujojs/when

https://promisesaplus.com/implementations

OR

https://6to5.org/

https://github.com/google/traceur-compiler

What if I already have callbacks?

Return a Promise from a native callback

function returnPromise(input)
    return new Promise(function(resolve, reject) {
        someFunction(input, function(err, result) {
            if (err) { return reject(err); }
            else { return resolve(result); }
        });
    });
}

The other way around

Have a promise call a callback

function returnCallback(input, cb) {
    someFunction(input)
    .then(function(result) {
        cb(undefined, result);
    })
    .catch(function(err) {
        cb(err);
    });
}

Party Tricks

function multiHeadedPromise(medusa, hydra) {
    var promiseHolder;

    if (medusa) {
        promiseHolder = medusa.stare();
    }
    else {
        promiseHolder = hydra.swallow();
    }

    promiseHolder
    .then(function(monster) {
        return kill-9(monster);
    })
    .catch(function(err) {
        console.log('i die.');
    });

}

Party Tricks

function PromiseMap(finalFantasySummons) {

    return Promise.map(finalFantasySummons, function(summon) {
        return summon.name;
    })
    .then(function(summonNames) {
        console.log(summonNames);
        // ['bahamut', 'tiamat', 'shiva', 'ifrit']
        return summonNames;
    })
    .then(function(summonNames) {
        return Promise.each(summonNames, function(name) {
            return summon(name);
        });
    })
    .catch(function(err) {
        console.log('summons failed.');
    });

}

Party Tricks

function AdvancedErros(badInput) {
    return new Promise(function(resolve, reject) {
        var valid = testInput(badInput);
        if (valid) { return resolve(badInput); }
        else { throw new Promise.OperationalError('Input is naughty'); }
    })
    .then(function(validInput) {
        // do something with input
    })
    .then(function(result) {
        var validResult = testResult(result);
        if (valid) { return result; }
        else { throw new Error('This shouldn't happen'); }
    })
    .error(function(err) {
        console.log('Input was naughty');
    })
    .catch(function(err) {
        console.log('Something that shouldn't happen, happened.');
    });
    .then(function() {
       return 'something anyway'; 
    });
}

Behind the Scenes

I've been leading you on

SomePromise()
// .then(function(resolve), function(reject), function(notify))
.then(function(){});
// --> .then(function(){}, null, null);
.catch(function(err) {});
//--> .then(null, function(err) {}, null);


SomePromise()
.then(console.log)
.catch(console.err)
.then(wat);

Behind the Scenes

I've been leading you on

var Promise = require('bluebird');
var db = require('pg');

{
    method: 'POST',
    path: '/test',
    config: {
        validate: { payload: { user: Joi.object().required() } },
        handler: function(request, reply) {
            var user = request.payload.user;
            var promise = stupidFunction()
            .then(function(user) {
                return db.getUser(user); // returns user successfully
            })
            .catch(BadInputError, function(err) { return Boom.badRequest('Bad Input'); })
            .catch(function(err) { return Boom.notFound('Stupid Function'); });
            return reply(promise);
        }
    }
}
Made with Slides.com