Skip to content

Instantly share code, notes, and snippets.

@ljharb

ljharb/gist:8174372

Created Dec 29, 2013
Embed
What would you like to do?
Handling the results of multiple async ops with JS.
var promises = [
async1(),
async2(),
asyncN()
];
/* jQuery: warning, won't swallow exceptions */
var deferred = $.Deferred();
$.when.apply($, promises)
.done(function () { deferred.resolve(promises); })
.fail(function () { deferred.reject(promises); });
return deferred.promise();
/* Q: Promises/A+ compliant, plus more */
/* will resolve with an array of all values
or reject on first failure */
var all = Q.all(promises);
/* or, to wait for all to finish: */
var handleResults = function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
};
Q.allSettled(promises).then(handleResults);
/* Note that with solely a spec-compliant Promise, you'd have to write the code to aggregate with arrays yourself. Luckily, there's libraries to do that for you! */
@ljharb

This comment has been minimized.

Copy link
Owner Author

@ljharb ljharb commented Dec 29, 2013

Assumed is that all the async methods return promises themselves - and with the jQuery example, you'd have to inspect each promise - whereas with the Q example, you just get values and errors directly.

@aredridel

This comment has been minimized.

Copy link

@aredridel aredridel commented Dec 29, 2013

Thank you!

It doesn't look any easier than callback soup at that point, since you then have to start looping over the failures explicitly (instead of handling them in the callback, more or less implicitly)

(What I'm writing has some rollback semantics: some successes must be un-done if other things fail. And all the errors need to get reported.)

@aredridel

This comment has been minimized.

Copy link

@aredridel aredridel commented Dec 29, 2013

What would all be there? Another promise?

@ljharb

This comment has been minimized.

Copy link
Owner Author

@ljharb ljharb commented Dec 29, 2013

Yes, Q.all returns a promise, as does Q.allSettled and $.when.

Here's a pretty straightforward way to report all errors so you can roll back if necessary: (obviously it's N loops so you could do it more efficiently, but I'm always willing to sacrifice efficiency for readability)

Q.allSettled(promises).then(function (results) {
    var allSucceeded = results.every(function (result) { return result.state === 'fulfilled'; });
    if (!allSucceeded) {
        var errorResults = results.filter(function (result) { return result.state !== 'fulfilled'; });
        var errors = errorResults.map(function (result) { return result.reason; });
        rollback(errors);
    } else {
        commit();
    }
});

As for "easier" versus "harder", it's that with callback soup (ps i highly recommend reading http://callbackhell.com) the cognitive load to understand what's happening throughout the program is much higher. With promises, even if it's the same amount of code, it's much simpler to look at a line and see "input leads to output" - which should be much simpler to understand.

@aredridel

This comment has been minimized.

Copy link

@aredridel aredridel commented Dec 29, 2013

Yeah, maybe I'm weird, but I see about the same amount of complexity each way.

I'll keep thinking about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment