JavaScript Promise Usage

  • 2021-06-28 10:43:35
  • OfStack

Synchronous programming is generally easy to debug and maintain, however, asynchronous programming often results in better performance and greater flexibility.The most important feature of asynchronization is that there is no need to wait.Promises"has gradually become the most important part of JavaScript, and a large number of new APIs have begun to implement the promise principle.Let's take a look at what promise is, and its API and usage!

Promises Status Quo

XMLHttpRequest API is asynchronous, but it does not use promise API.However, many native javascript API use promise:

*Battery API
*fetch API (an alternative to XHR)
*ServiceWorker API

Promises will only become more and more popular and important in the future, and will be used by all front-end developers.Another notable feature is that Node.js is an Promises-based platform (obviously, Promise is one of its core features).

The use of Promises is simpler than you think -- if you used to like using setTimeout to control asynchronous tasks!

Promise Basic Usage

The new Promise () constructor can be used in traditional asynchronous tasks, just like the previous usages of setTimeout and XMLHttpRequest 1.A new Promise is generated using the new keyword, which also provides the resolve and reject functions to allow us to perform callback operations:


var p = new Promise(function(resolve, reject) {
 
 // Do an async task async task and then...

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

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

Programmers can manually call resolve and reject functions within callback functions based on execution.Here is a more practical example of converting an XMLHttpRequest call to an Promises-based task:


// 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);
});

Promise.resolve() and Promise.reject() can be called directly.Sometimes, when it is determined that promise does not really need to be executed, we do not need to create an Promise object with new, but can call Promise.resolve() and Promise.reject() directly.For example:


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 returns 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);
  });
}

Since promise will definitely return, we can use the then and catch methods to process the return values!

then method

All promise object instances have an then method that is used to interact with this promise.First, the then method calls the resolve() function by default:


new Promise(function(resolve, reject) {
 // A mock async action using setTimeout
 setTimeout(function() { resolve(10); }, 3000);
})
.then(function(result) {
 console.log(result);
});

// From the console:
// 10

The trigger time for the then callback action is when promise has been executed.We can also concatenate the then method to perform callback operations:


new Promise(function(resolve, reject) { 
 // A mock async action using setTimeout
 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

You will find that each then call takes a parameter with the return value of the previous then call.

If an promise has been executed and a single then is called again, the callback action will be executed again.If the reject callback function is executed in this promise, which calls the then method again, the callback function will not be executed.

catch method

catch When an promise is rejected (reject), the catch method is executed:


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); });

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

Usually we process the result of execution failure in the reject method, while execute the exception in catch:

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

Promise.all Method

There is often a scenario when we make asynchronous calls where we need to call multiple asynchronous operations at the same time, but we want to wait until all operations are completed before we can perform a response operation -- that's what Promise.all does.The Promise.all method can take multiple promises as parameters and use them as arrays to invoke callback functions after they have been successfully executed.


Promise.all([promise1, promise2]).then(function(results) {
 // Both promises resolved
})
.catch(function(error) {
 // One or more promises was rejected
});

A good example that demonstrates the use of Promise.all is to perform multiple AJAX operations (called through fetch):


var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');

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

We can also mix fetch with battery state API 1 because they all return promise:


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

1Once the reject function is called in promise, that is, execution is rejected and can not be completed properly, the situation will be somewhat complicated.1Once promise is rejected, the catch method captures the first reject function to be executed:


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: ', one);
}).catch(function(err) {
 console.log('Catch: ', err);
});

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

Promise.all is a very important interface and will play an important role in many newly born promise API.

Promise.race

Promise.race is an interesting function - instead of waiting for all promises to be resolve or reject, it triggers whenever one of all promises finishes execution:


// 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);
});

0

A useful scenario is to download resources from multiple mirror servers, and once one return occurs, the other returns are not processed.

Learn to use Promises

Promises has been a hot topic in the past few years, and it has even been pulled out of JavaScript to become a language architecture.We believe that we will soon see more and more JavaScript API using promise-based models.


Related articles: