Promises – with Q



Promises – with Q

0 0


promise-lab

A lab and presentation about Q promises

On Github andersjanmyr / promise-lab

Promises

with Q

Anders Janmyr http://anders.janmyr.com @andersjanmyr

Promises

What?

Promises, what?

A promise is an object that represents the return value or the thrown exception that the function may eventually provide.

Promises

Why?

Promises, why?

To bring composability and error handling to asynchronous programming.

Callback (successful)

function asyncHello(what, callback) {
    setTimeout(function() {
        callback(null, 'Hello ' + what);
    }, 100);
}
asyncHello('Tapir', function(err, hello) {
    if (err) return console.error('Failure');
    console.log(hello);
});

Callback (failing)

function asyncFail(what, callback) {
    setTimeout(function() {
        callback('I fail, therefore I am');
    }, 100);
}
asyncFail('Tapir', function(err, hello) {
    if (err) return console.error('Failure');
    // do something
});

Callback (error)

function asyncError(what, callback) {
    try {
        return callback(null, JSON.parse(what));
    } catch (error) {
        return callback(error);
    }
}

Then

promise.then(onFulfilled, onRejected)

catch

promise.catch(onRejected)

// Same as
promise.then(null, onRejected)

finally

promise.then(onFulfilled, onRejected)
.finally() // Clean up resources

Propagation

  • If you return a value in a handler, outputPromise will get fulfilled.
  • If you throw an exception in a handler, outputPromise will get rejected.
  • If you return a promise in a handler, outputPromise will “become” that promise. Being able to become a new promise is useful for managing delays, combining results, or recovering from errors.

Chaining and nesting

function authenticate() {
    return getUsername()
    .then(function (username) {
        return getUser(username);
    })
    // chained because we will not need the user name in the next event
    .then(function (user) {
        return getPassword()
        // nested because we need both user and password next
        .then(function (password) {
            if (user.passwordHash !== hash(password)) {
                throw new Error("Can't authenticate");
            }
        });
    })
    .catch(errorHandler);
}

Creating Q(value)

  • If value is a Q promise, returns the promise.
  • If value is a promise from another library it is coerced into a Q promise (where possible).
  • If value is not a promise, returns a promise that is fulfilled with value.

Creating (fcall)

// Creates a promise from a value
return Q.fcall(function () {
    return 10;
});

Creating (deferred)

// Interfacing with async functions
var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
    if (error) {
        deferred.reject(new Error(error));
    } else {
        deferred.resolve(text);
    }
});
return deferred.promise;

Creating with Node functions

return Q.nfcall(FS.readFile, "foo.txt", "utf-8");
return Q.nfapply(FS.readFile, ["foo.txt", "utf-8"]);

Creating with nbind

var Kitty = mongoose.model("Kitty");
var findKitties = Q.nbind(Kitty.find, Kitty);

findKitties({ cute: true }).done(function (theKitties) {

});

Combination (all)

return Q.all([
    eventualAdd(2, 2),
    eventualAdd(10, 20)
])
.then(function(resultArray);
then gets an array of results.

Combination (spread)

Q.all([getFromDisk(), getFromCloud()])
.spread(function (diskVal, cloudVal) {
    assert(diskVal === cloudVal);
})
.done();

Sequences

foo(initialVal).then(bar).then(baz).then(qux);

var funcs = [foo, bar, baz, qux];

//With a loop
var result = Q(initialVal);
funcs.forEach(function (f) {
    result = result.then(f);
});
return result;

Sequences

var funcs = [foo, bar, baz, qux];

//With reduce
return funcs.reduce(function (soFar, f) {
    return soFar.then(f);
}, Q(initialVal));

// Or compact
return funcs.reduce(Q.when, Q());

Q.when

// when is the static equivalent of then
return Q.when(valueOrPromise, function (value) {
}, function (error) {
});

End (Promises)

with Q

Anders Janmyr http://anders.janmyr.com @andersjanmyr