Javascript Promises Are Awesome

仲孙鸿畴
2023-12-01

http://ghost.deceptacle.com/2014/03/26/javascript-promises-are-awesome/

26 Mar 2014

I have completely fallen in love with Javascript promises. There's plenty of other blog posts going into more depth about the pros and cons of promises, so I'll include some relevant ones that I enjoyed at the bottom, but I just wanted to outline a few features/tricks that I use that show some of their power.

Multiple Asynchronous Calls

A promise library can let you wrap an array of promises in a larger promise, letting you fire off a number of asynchronous requests, and then processing them once they are all completed.

Example

// Populate an array with some promises
var promises = [];  
promises.push(getTrack(1));  
promises.push(getTrack(2));  
promises.push(getTrack(3));

Q.all(promises)  
.then(function (tracks) {
    // All of my tracks are loaded!
})
.fail(function (err) {
    // Oh no! A lookup failed :(
});

Easy as pie!

If, for some reason, I don't want to fail the entire chain if one promise lookup fails, Q provides a function called allSettled, which works similarly to all, but returns an object with each result wrapped up. Using the array from above:

Q.allSettled(promises)  
.then(function (results) {
    results.forEach(function (result) {
        if (result.state === 'fulfilled') {
            // This item was loaded!
            console.log(result.value);
        } else {
            // This item failed to be loaded :(
            console.log(result.reason);
        }
   });
});

Recursive Promise Resolution

This is one of those "oh I'm so clever" hacks that I came up with a little while back and have been refining it a bit. The basic idea is that you can use promises recursively to continue resolving an asynchronous call if you need to (for example, searching through paged data, or handling failed calls).

Here's an example that I'm using on a personal project:

// This function retrieves data from the provided array of URIs
var get = function (uris, depth) {  
    var promises = [];
    depth = depth || 0;

    // Make sure we don't recurse forever!
    if (depth >= 3) {
        // Return a promise that returns an empty array
        return Q.fcall(function () {
            return [];
        });
    }

    uris.forEach(function(uri) {
        // The lookup function is what actually makes a
        // singular request
        promises.push(lookup(uri));
    });

    return Q.allSettled(promises)
    .then(function (results) {
        var failed = [];
        var succeeded = [];

        results.forEach(function (result) {
            if (result.state === 'fulfilled') {
                succeeded.push(result.value);
            } else {
                // Note: the lookup call adds the uri to the
                // error object
                failed.push(result.reason.uri);
            }
        });

        if (failed.length === 0) {
            // No failed lookups, just pass along the succeeded items
            return succeeded;
        } else {
            return get(failed, depth + 1)
            .then(function join(results) {
                // Join the results from the recursive call to the 
                // succeeded object

                return succeeded.concat(results);
            });
        }
    });
}

I know this is a fair amount to process, but I think it's pretty self explanatory overall. If all the calls succeed without an issue, then it works much like the example above regarding the Multiple Asynchronous Calls. Where this shines is when one of those calls fails, we can just continue returning promises until the call succeeds, or we've tried too many times. We take the items pushed into the failed array, and return a recursive call to get, which returns a promise. Whatever initially called get will continue processing when we've tried fetching the data multiple times.

Now, this could be fleshed out a bit to return the failed results if needed, but that wouldn't be too difficult. It would probably follow a similar output as the allSettled function, providing a wrapper around the result set.

These were just two things about promises that I really love. There are a number of other ways that promises can be leveraged to make asynchronous code easier to manage, but these two are some recent examples that I've used recently, and wanted to share them.

  • Callback Hell - Not specifically about promises, but shows what can happen if you let callbacks get out of hand.
  • Promises/A+ Spec
  • What's So Great About Javascript Promises - Focuses more on the jQuery implementation of promises. Talks about some of the powerful features though.
  • You're Missing the Point of Promises - Great outline about what promises provide, aside from the commonly known then function.
  • Q - My favorite promises implementation. Supports a number of platforms (Node and Browser environments, including AMD loading).

Andrew Burgess

  • Los Angeles, CA

 类似资料:

相关阅读

相关文章

相关问答