In general, querying network API operations is time consuming in development, which means that it may take some time to get a response. Thus, asynchronous programming becomes a basic skill for developers in order to avoid situations where a program does not respond when requested.

When dealing with asynchronous operations in JavaScript, we often hear the term “Promise “. But understanding how it works and how to use it can be abstract and difficult to understand.

So, in this article we’ll take a hands-on approach to getting you up to speed on their concepts and usage, so unlike many traditional dry tutorials, we’ll start with four examples:

  • Example 1: Use birthdays to explain the basics of promises
  • Example 2: A number guessing game
  • Example 3: Get country information from a Web API
  • Example 4: Get a list of countries around a country from the Web API

Example 1: Use birthdays to explain Promise basics

First, let’s take a look at the basic form of promises.

This Promise is implemented in three states: pending, fulfilled, and rejected.

New Promise(function(resolve, reject) {if (/* async */) {resolve(value); // Change the state of Promise from padding to pity} else {reject(error); // change the state of the Promise from padding to rejected}})Copy the code

There are three prototype methods implemented: then, Catch, and finally

Then ((result) => {// a promise is accepted or rejected to continue}). Catch ((error) => {// a promise is rejected}).finally (() => { // When a promise is completed, it will work no matter what})Copy the code

Now that the basic form is covered, let’s start with the following example.

User Story: My friend Kayo promised to make me a cake for my birthday Party in two weeks.

If everything goes well and Kayo doesn’t get sick, we get a certain amount of cake, but if Kayo gets sick, we don’t get any cake. But cake or no cake, we’ll still have a birthday Party.

So for this example, translating the above backstory into JS code, let’s start by creating a function that returns Promise.

const onMyBirthday = (isKayoSick) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (!isKayoSick) {
        resolve(2);
      } else {
        reject(new Error("I am sad"));
      }
    }, 2000);
  });
};
Copy the code

In JavaScript, we can create a new Promise using new Promise(), which takes a function with the argument :(resolve, reject)=>{}.

In this function, resolve and reject are the callbacks provided by default. Let’s take a closer look at the code above.

When we run the onMyBirthday function 2000ms later.

  • If Kayo is not sick, then we execute resolve with an argument of 2
  • If Kayo is sick, we execute reject with new Error(“I am sad”) as the argument. Although you can pass anything you want to reject as a parameter, it is recommended that you pass it to the Error object.

Now, because onMyBirthday() returns a Promise, we can access the then, catch, and finally methods. We also have access to the arguments passed to resolve and Reject that were used earlier in THEN and catch.

Let’s look at the following code to understand the concept

If Kayo hadn’t been sick

onMyBirthday(false) .then((result) => { console.log(\`I have ${result} cakes\`); // console prints "I have 2 cakes"}). Catch ((error) => {console.log(error); }).finally(() => {console.log("Party"); // console prints "Party"});Copy the code

If Kayo gets sick

onMyBirthday(true) .then((result) => { console.log(\`I have ${result} cakes\`); / / not perform}). The catch ((error) = > {the console. The log (error); / / console print "I'm sorry"}) finally (() = > {the console. The log (" Party "); // console prints "Party"});Copy the code

I’m sure you’ll get an idea of the basic concept of Promise from this example.

Let’s start with example 2

Example 2: A number guessing game

Basic needs:

  • The user can enter any number
  • The system randomly generates a number from 1 to 6
  • If the user enters a number equal to the system random number, 2 points are given to the user
  • If the difference between the number entered by the user and the system random number is 1, the user is given 1 score; otherwise, the user is given 0 score
  • Users can play as long as they want

For the above requirements, we first create an enterNumber function and return a Promise:

Const enterNumber = () => {return new Promise((resolve, reject) => {// start from this}); };Copy the code

The first thing we do is ask the user for a number and pick a random number between 1 and 6:

const enterNumber = () => { return new Promise((resolve, reject) => { const userNumber = Number(window.prompt("Enter a number (1 - 6):")); Const randomNumber = math.floor (math.random () * 6 + 1); // Ask the user for a number const randomNumber = math.floor (math.random () * 6 + 1); // Select a random number from 1 to 6}); };Copy the code

When the user enters a value that is not a number. In this case, we call reject and throw an error:

const enterNumber = () => { return new Promise((resolve, reject) => { const userNumber = Number(window.prompt("Enter a number (1 - 6):")); Const randomNumber = math.floor (math.random () * 6 + 1); // Ask the user for a number const randomNumber = math.floor (math.random () * 6 + 1); // Select a random number from 1 to 6 if (isNaN(userNumber)) {reject(new Error("Wrong Input Type")); // When the user enters a non-numeric value, throw an exception and call reject}}); };Copy the code

Next, we need to check if the userNumber is equal to RanomNumber, if so, we give the user 2 points, and then we can execute resolve to pass an object {points: 2, randomNumber} object.

If the userNumber differs from randomNumber by 1, then we give the user 1 point. Otherwise, we give the user 0 points.

return new Promise((resolve, reject) => { const userNumber = Number(window.prompt("Enter a number (1 - 6):")); Const randomNumber = math.floor (math.random () * 6 + 1); // Ask the user for a number const randomNumber = math.floor (math.random () * 6 + 1); // Select a random number from 1 to 6 if (isNaN(userNumber)) {reject(new Error("Wrong Input Type")); } if (userNumber === randomNumber) {// If (userNumber === randomNumber) {// If (userNumber === randomNumber) { 2, randomNumber, }); } else if (userNumber = = = randomNumber - 1 | | userNumber = = = randomNumber + 1) {/ / if userNumber differ with randomNumber 1, Resolve ({points: 1, randomNumber,}); Resolve ({points: 0, randomNumber,}); }});Copy the code

Now, let’s create another function to ask the user if they want to continue playing:

const continueGame = () => { return new Promise((resolve) => { if (window.confirm("Do you want to continue?" // Ask the user whether to continue the game resolve(true); } else { resolve(false); }}); };Copy the code

In order not to force the game to end, the Promise we created didn’t use the Reject callback.

Let’s create a function to handle the guess number logic:

Const handleGuess = () => {enterNumber() // Returns a Promise object. Then ((result) => {alert(\ 'Dice: ${result.randomNumber}: you got ${result.points} points\`); ContinueGame (). Then ((result) => {if (result) {handleGuess(); if (result) {handleGuess(); } else {alert("Game ends"); // If no, popup game end box}}); }) .catch((error) => alert(error)); }; handleGuess(); // Execute the handleGuess functionCopy the code

Here, when we call handleGuess, enterNumber() returns a Promise object.

If the Promise state is Resolved, we call the then method to tell the user the result and score, and ask the user if they want to continue.

If the Promise status is Rejected, we display a message indicating that the user entered an error.

However, while such code solves the problem, it is still a bit difficult to read. Let’s use async/await to refactor hanldeGuess later.

There are many explanations of async/await on the Internet. Here I would like to use a simple and general explanation: Async /await is a syntactic sugar that can turn complex and difficult asynchronous code into synchro-like syntax.

Let’s start with the refactored code:

const handleGuess = async () => { try { const result = await enterNumber(); Alert (\ 'Dice: ${result.randomNumber}: you got ${result.points} points\'); const isContinuing = await continueGame(); if (isContinuing) { handleGuess(); } else { alert("Game ends"); }} catch (error) {// The catch method can be a try, catch function instead of alert(error); }};Copy the code

We create an asynchronous function by using the async keyword before the function, which is used differently within the function than before:

  • Unlike the then function, we simply place the await keyword before the Promise and get the result directly.
  • We can use the try catch syntax instead of the catch method in promise.

Here is the complete code after our refactoring for your reference:

const enterNumber = () => { return new Promise((resolve, reject) => { const userNumber = Number(window.prompt("Enter a number (1 - 6):")); Const randomNumber = math.floor (math.random () * 6 + 1); // Ask the user for a number const randomNumber = math.floor (math.random () * 6 + 1); If (isNaN(userNumber)) {reject(new Error("Wrong Input Type")); If (userNumber === randomNumber) {resolve({points: 2, randomNumber,}); if (userNumber === randomNumber) { } else if (userNumber = = = randomNumber - 1 | | userNumber = = = randomNumber + 1) {/ / if userNumber differ with randomNumber 1, Resolve ({points: 1, randomNumber,}); Resolve ({points: 0, randomNumber,}); }}); }; const continueGame = () => { return new Promise((resolve) => { if (window.confirm("Do you want to continue?" // Ask the user whether to continue the game resolve(true); } else { resolve(false); }}); }; const handleGuess = async () => { try { const result = await enterNumber(); \ 'Dice: ${result.randomNumber}: you got ${result.points} points\'); // await instead of then function alert(\ 'Dice: ${result.randomNumber}: you got ${result.points} points\'); const isContinuing = await continueGame(); if (isContinuing) { handleGuess(); } else { alert("Game ends"); }} catch (error) {// The catch method can be a try, catch function instead of alert(error); }}; handleGuess(); // Execute the handleGuess functionCopy the code

Now that we’ve completed the second example, let’s move on to the third.

Example 3: Get country information from a Web API

Promises are great for developers to use when getting data from apis. If the new window open restcountries. Eu/rest/v2 / alp…

Using the Fetch API, we can easily get data. Here is the code:

const fetchData = async () => {
  const res = await fetch("https://restcountries.eu/rest/v2/alpha/cn"); // fetch() returns a promise, so we need to wait for it

  const country = await res.json(); // res is now only an HTTP response, so we need to call res.json()

  console.log(country); // China's data will be logged to the dev console
};

fetchData();
Copy the code

Now that we have the country/area data we need, let’s move on to the last task.

Example 4: Get a list of countries around a country from the Web API

The fetchCountry function below fetchCountry gets the country information from the API in example 3, where the argument alpha3Code is the country code for that country. Here is the code

// Task 4: Const fetchCountry = async (alpha3Code) => {try {const res = await fetch( \`https://restcountries.eu/rest/v2/alpha/${alpha3Code}\` ); const data = await res.json(); return data; } catch (error) { console.log(error); }};Copy the code

Let’s create a fetchCountryAndNeighbors function to get information about China by passing CN as alpha3code.

const fetchCountryAndNeighbors = async () => {
  const china= await fetchCountry("cn");

  console.log(china);
};

fetchCountryAndNeighbors();
Copy the code

In the console, let’s look at the object content:

In the object, there is a border property, which is the list of Alpha3Codes for China’s neighboring countries.

Now, if we try to get information about neighboring countries in the following way.

const neighbors = 
    china.borders.map((border) => fetchCountry(border));
Copy the code

Neighbors is an array of Promise objects.

When dealing with an array of promises, we need to use promise.all.

const fetchCountryAndNeigbors = async () => {
  const china = await fetchCountry("cn");

  const neighbors = await Promise.all(
    china.borders.map((border) => fetchCountry(border))
  );

  console.log(neighbors);
};

fetchCountryAndNeigbors();
Copy the code

In the console, we should be able to see a list of country/region objects.

Here is all the code for example 4 for your reference:

const fetchCountry = async (alpha3Code) => { try { const res = await fetch( \`https://restcountries.eu/rest/v2/alpha/${alpha3Code}\` ); const data = await res.json(); return data; } catch (error) { console.log(error); }}; const fetchCountryAndNeigbors = async () => { const china = await fetchCountry("cn"); const neighbors = await Promise.all( china.borders.map((border) => fetchCountry(border)) ); console.log(neighbors); }; fetchCountryAndNeigbors();Copy the code

conclusion

After completing these four examples, you can see how Promise can be useful when dealing with asynchronous operations or things that don’t happen at the same time. I believe that in continuous practice, the understanding of it will be deeper and stronger, I hope this article can help you understand Promise and Async/Await.

Here is the code used in this article:

Promise-Async-Await-main.zip

Further reading

SpreadJS (SpreadJS), a pure front-end table control with a functional layout highly similar to Excel, is recommended.www.grapecity.com.cn/developer/s… )