Post

JavaScript Promise API

JavaScript Promise API

This article was translated from https://davidwalsh.name/promises

While synchronized code is easier to trace and debug, asynchronous is often better for performance and flexibility. Why stop and wait when you can launch multiple requests right away and then process them when each one is ready?Promises are becoming an important part of the JavaScript world, and there are a number of new APIs being implemented with the Promise philosophy. Let’s take a look at how promises and their APIs are used below!

Promises in the natural environment

The XMLHttpRequest API is asynchronous, but doesn’t use the Promises API. however, there are now native APIs that use promises:

Promises is only going to become more common, it’s important, and all front-end developers should be familiar with it. It’s also worth noting that Node.js’s is another platform for Promises (obviously, Promises is a core language feature).

_Testing promises is easier than you think because setTimeout can be your asynchronous “task”! _

**Basic Usage of Promise###

The new Promise() constructor should only be used for traditional asynchronous tasks such as setTimeout or XMLHttpRequest usage. That is, the keyword new is used to create a new Promise object that provides the resolve and reject callback functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var p = new Promise(function(resolve, reject) {
    // Do an async task async task and then...

    if(/* good condition */) {
    	resolve('Succes!';)
    }
    else {
    	reject('Failure!');
    }
});

p.then(function() {
    /* do something with the result */
}).catch(function() {
    /* error :( */
})

Depending on the result of the developer’s asynchronous task execution, resolve or reject is called manually in the callback function. A typical example is the task of converting an XMLHttpRequest into a promise-based task:

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
// From Jake Archibald's Promises and Back:
// https://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 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);
}, fucntion(error) {
    console.log("Failed!", error);
});

Sometimes, you don’t need to perform an asynchronous task within a promise —- However, if it is possible to perform an asynchronous task, it would be best practice to return a promise object so that you only need to give the result of the handler function would be sufficient. In this case, there is no need to use the keyword new you can simply call Promise.resolve() or Promise.reject(). Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var userCache = {};

function getUserDetail(username) {
    // In both cases, cached or not, a promise will be returned

    if (userCache[username]) {
    	// Return a promise without the "new" keyword
    	return Promise.resolve(userCache[username]);
    }

    // Use the fetch API to get the information
    // fetch return a promise
    return fetch('users/' + username + '.json')
    	.then(function(result) {
    		userCache[username] = result;
    		return result;
    	})
    	.catch(function() {
    		throw new Error('Could not find user: ' + username);
    	})
}

Because a promise is always returned, you can always use the then and catch methods on its return value!

##then## All promise instances have a then method that handles the result of the execution. The first then method callback receives the result of the resolve() call as an argument:

1
2
3
4
5
6
7
8
9
10
11
12
new Promise(function(resolve, reject) {
    // A mock async acton using setTimeout
    setTimeout(function() {
    	resolve(10);
    }, 3000);
})
.then(function(result) {
    console.log(result);
})

// From the console
// 10

The then callback function is triggered when the resolve of the promise is triggered. You can also use chained then callback methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
new Promise(function(resolve, reject) {
    setTimeout(function() {
    	resolve(10);
    }, 3000)
})
.then(function(num) {
    console.log('first then: ', num);
    return num * 2;
})
.then(function(num) {
    console.log('second then: ', num);
    return num * 2;
})
.then(function(num) {
    console.log('last then: ', num);
});

// From the console
// first then: 10
// second then: 20
//last then: 40

Each then receives the result of the previous then return value.

If the resolve of a promise has been triggered, but then is called again afterward, the callback will be triggered immediately. If the promise is rejected and you call then after the rejection, the callback will not be invoked.

catch

The catch callback function is executed when promise is rejected:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new Promise(function(resolve, reject) {
    // A mock async action using setTimeout
    setTimeout(function() {
    	reject('Done!');
    }, 3000);
})
.then(function(e) {
    console.log('done', e);
})
.catch(function(e) {
    console.log('catch: ', e);
})

// Frome the console:
// 'catch: Done!'

The parameters passed into the reject method are up to you. Typically an Error object` is passed in:

1
reject(Error('Data could not be found'));

Promise.all

Think about JavaScript loader scenarios: there are times when you trigger multiple asynchronous interactions, but only want to respond once they’re all done —- This is where Promise.all comes in handy. The Promise.all method takes an array of promises and executes a callback function once their resolve has been triggered:

1
2
3
4
5
6
Promise.all([promise1, promise2]).then(function(results) {
    // Both promises resolved
})
.catch(function(error) {
    // One or more promises was rejected
})

The best example of using Promise.all is (via fetch) when initiating multiple AJAX requests at the same time:.

1
2
3
4
5
6
var request1 = fetch('/user.json');
var request2 = fetch('./articles.json');

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

You can combine APIs like fetch and the Battery API as they both return promises:

1
2
3
4
Promise.all([fetch('./users.json'), navigator.getBattery()])
    .then(function(results) {
    	// Both promises done!
    });

Handling rejections is of course complex. If any promise is rejected, catch will be triggered by the first rejection.

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

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

As more and more APIs support promises, Promise.all will become very useful.

Promise.race

Promise.race is an interesting function —- Once any Promise in the array is resolved or rejected, Promise.race fires without waiting for all the Promise objects are resolved or rejected.

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

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

The use case may initiate a request to both the primary and standby resources (in case one of the primary and standby is unavailable).

Familiarize yourself with Promise

Promises have been hotly debated for the last few years (or the last 10 if you’re a Dojo Toolkit user), and they’ve gone from being part of a JavaScript framework to being a major part of a JavaScript language. In all likelihood, you’ll see most of the new JavaScript APIs implemented based on promises…

…… This is a great thing! Developers are able to avoid callback hell and asynchronous interactions can be passed around like any other variable.Promise will take a while to catch on, now is the time to learn about them!

Concluding remarks I think the benefits of translation for me are It makes me keep thinking, look at the first-hand information seriously, and I can slowly read other cutting-edge articles. To learn more about my work, click on the menu About

This post is licensed under CC BY 4.0 by the author.