Translation of the first part of an excellent article about promises. Basic techniques for creating and managing promises.
Promises are used for operations whose calculation takes indefinite time. An example of such an operation could be a network request when we request data from the API and cannot determine exactly when a response will be received.
If there are other operations that depend on this network request, then a problem will appear. Without promises, we have to use a string of callbacks to build a sequence of operations. This is normal if we have one asynchronous action. But if you need to take several consecutive asynchronous steps, callbacks become unmanageable and the result is notorious as callback hell
doSomething(function(responseOne) { doSomethingElse(responseOne, function(responseTwo, err) { if (err) { handleError(err); } doMoreStuff(responseTwo, function(responseThree, err) { if (err) { handleAnotherError(err); } doFinalThing(responseThree, function(err) { if (err) { handleAnotherError(err); } // }); // doFinalThing }); // doMoreStuff }); // doSomethingElse }); // doSomething
Promises provide a standardized and understandable method for solving problems that must be performed consistently.
doSomething() .then(doSomethingElse) .catch(handleError) .then(doMoreStuff) .then(doFinalThing) .catch(handleAnotherError)
Promises are created using the constructor of promises. It is a function with two arguments ( resolve
& reject
) as parameters.
var promise = new Promise(function(resolve, reject) { /* */ } )
Inside this function, we can perform any asynchronous tasks. To mark promises as executed , we call resolve()
, passing it the value we want to return. To mark a promise as rejected or unsuccessful, we call reject()
, passing an error message to it. Before the promise is executed or rejected, it is in a state of waiting .
Here is the promis version of XMLHttpRequest -
/* CREDIT - Jake Archibald, http://www.html5rocks.com/en/tutorials/es6/promises/ */ function get(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); /* */ } else { reject(Error(req.statusText)); /* */ } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); }); }
To do a promise, we can call it like any normal function. But, since this is a promise, we have access to a .then
method that we can add to the function and that will be executed when the promise goes out of standby mode.
.then()
method takes two optional parameters. The first is the function that is called when the promise is executed. The second is a function that is performed if the promise is rejected.
get(url) .then(function(response) { /* successFunction */ }, function(err) { /* errorFunction */ })
Since both parameters (successFunction and errorFunction) are optional, we can divide them into two .then()
for better readability.
get(url) .then(function(response) { /* successFunction */ }, undefined) .then(undefined, function(err) { /* errorFunction */ })
To make the code even more understandable, we can use the .catch()
method, which is a shortcut for .then(undefined, errorFunction)
get(url) .then(function(response) { /* successFunction */ }) .catch(function(err) { /* errorFunction */ })
The real value of promises is that we can perform several asynchronous functions in order. We can combine .then()
and .catch()
together to create a sequence of asynchronous functions.
We can do this by returning another promise after completing or rejecting the previous one. For example -
get(url) .then(function(response) { response = JSON.parse(response); var secondURL = response.data.url return get( secondURL ); /* */ }) .then(function(response) { response = JSON.parse(response); var thirdURL = response.data.url return get( thirdURL ); /* */ }) .catch(function(err) { handleError(err); });
If the resolved is executed, the nearest .then()
in the sequence will be called. If the promise is rejected, then the nearest .catch()
in sequence.
A situation may arise when we need to perform several promises in parallel, and continue the algorithm only after all promises are completed. For example, if we want to get a series of images and only after that display them on the page.
To do this, we need to use two methods. This is Array.map()
, in order to apply a promise for each element of the array and save the result to a new array. And Promise.all()
, which will resolve()
if all promises are executed in an array. If at least one promise in the array is rejected, Promise.all()
will also be rejected.
var arrayOfURLs = ['one.json', 'two.json', 'three.json', 'four.json']; var arrayOfPromises = arrayOfURLs.map(get); Promise.all(arrayOfPromises) .then(function(arrayOfResults) { /* -, */ }) .catch(function(err) { /* , */ })
If we look into the Network panel of the Development tools, we will see that all requests happen in parallel.
If you need support for IE and / or Opera Mini, use a polyfill .
Thanks for attention!
Source: https://habr.com/ru/post/312670/
All Articles