Gangmax Blog

自由之思想,独立之精神

Promise in JavaScript

| Comments

Promise is a mechanism for JavaScript to avoid the “callback hell” problem in asynchronous operations.

What is a promise?

From here.

The core idea behind promises is that a promise represents the result of an asynchronous operation. A promise is in one of three different states:

  1. Pending: The initial state of a promise.

  2. Fulfilled(resloved): The state of a promise representing a successful operation.

  3. Rejected: The state of a promise representing a failed operation.

Once a promise is fulfilled or rejected, it is immutable (i.e. it can never change again).

Here is an example how to use it: (From here)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// From Jake Archibald's Promises and Back:
// http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promisifying-xmlhttprequest

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

// Use it!
get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
});

// or
get('story.json').then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.error("Failed!", error);
});

Note the following facts:

  1. When creating a “Promise” instance, pass a function as the constructor parameter which has two parameters, each of them is a function: The former(resolve/fulfill) is the one to be called when the async operation succeeds, which has one parameter that is the returned object of the async operation. The latter(reject) is the one to be called when the async operation fails, which has one parameter that is the error object.

  2. The “Promise” instance has the “then()” method, which will be called when the async operation finishes. The “then()” method accepts either two arguments “resolve/fulfill” and “reject”, or one argument “resolve/fulfill”. The “resolve/fulfill” parameter is a function has one parameter which is the returned object of the async operation. The “reject” parameter is a function has one parameter which is the error object.

  3. The “Promise” instance has the “catch()” method, which will be called when the async operation fails. The “catch()” method accepts one argument “reject”, which is a function has one parameter that is the error object.

What is “Promise.all”?

From here.

If you trigger multiple async interactions but only want to respond when all of them are completed, that’s where “Promise.all” comes in. The “Promise.all” method takes an array of promises and fires one callback once they are all resolved:

1
2
3
4
5
6
7
8
Promise.all([promise1, promise2]).then(function(results) {
  // Both promises resolved
})
.catch(function(error) {
  // One or more promises was rejected.
  // If there are more than one promises are rejected,
  // only the first error can be caught here.
});

An perfect way of thinking about Promise.all is firing off multiple AJAX (via fetch) requests at one time:

1
2
3
4
5
6
7
8
9
var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

Promise.all([request1, request2]).then(function(results) {
  // Both promises done!
});

// From the console:
// Catch: Second!

What is “Promise.race”?

From here.

“Promise.race” is an interesting function: instead of waiting for all promises to be resolved or rejected, “Promise.race” triggers as soon as any promise in the array is resolved or rejected:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var req1 = new Promise(function(resolve, reject) {
  // A mock async action using setTimeout
  setTimeout(function() { resolve('First!'); }, 8000);
});
var req2 = new Promise(function(resolve, reject) {
  // A mock async action using setTimeout
  setTimeout(function() { resolve('Second!'); }, 3000);
});
Promise.race([req1, req2]).then(function(one) {
  console.log('Then: ', one);
}).catch(function(one, two) {
  console.log('Catch: ', one);
});

// From the console:
// Then: Second!

A use case could be triggering a request to a primary source and a secondary source (in case the primary or secondary are unavailable).

Polyfill

In web development, a polyfill is code that implements a feature on web browsers that do not support the feature. Most often, it refers to a JavaScript library that implements an HTML5 web standard, either an established standard (supported by some browsers) on older browsers, or a proposed standard (not supported by any browsers) on existing browsers. Formally, “a polyfill is a shim for a browser API”.

Polyfills allow web developers to use an API regardless of whether it is supported by a browser or not, and usually with minimal overhead. Typically they first check if a browser supports an API, and use it if available, otherwise using their own implementation. Polyfills themselves use other, more supported features, and thus different polyfills may be needed for different browsers. The term is also used as a verb: polyfilling is providing a polyfill for a feature.

For an example, ”es6-promise” is a polyfill of Promise for ES6.

“async/await” in ES7 make it even simpler

ES7 introduce async and await syntax. It makes the asynchronous syntax look prettier and easier to understand, without the “then” and “catch”.

1
2
3
4
5
6
7
8
9
async function(request, response) {
    try {
        var user = await User.get(request.user);
        var notebook = await Notebook.get(user.notebook);
        response.send(await doSomethingAsync(user, notebook));
    } catch(err) {
        response.send(err);
    }
}

The simplest explanation of how this works is that await takes a promise, waits for it’s value to be available, and then returns that value.

Note:

  1. “await” takes a promise, waits for it’s value to be available, and then returns that value.

  2. only functions with the “async” keyword can have “await” statement inside. And you can’t have a top level “await” as well. From here.

And you can use “Promise.all” as well:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var P = require("popsicle");

async function y() {
  var sites = await Promise.all([
      P.get("http://www.google.com"),
      P.get("http://www.apple.com"),
      P.get("http://www.yahoo.com")
  ]);
  return sites;
}

var c = y();
const util = require('util')
util.inspect(c);

Comments