溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript中Promise和Async/Await怎么用

發(fā)布時間:2021-01-30 13:20:34 來源:億速云 閱讀:148 作者:小新 欄目:web開發(fā)

這篇文章主要介紹JavaScript中Promise和Async/Await怎么用,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們一定要看完!

一般在開發(fā)中,查詢網(wǎng)絡(luò)API操作時往往是比較耗時的,這意味著可能需要一段時間的等待才能獲得響應(yīng)。因此,為了避免程序在請求時無響應(yīng)的情況,異步編程就成為了開發(fā)人員的一項基本技能。

在JavaScript中處理異步操作時,通常我們經(jīng)常會聽到 "Promise "這個概念。但要理解它的工作原理及使用方法可能會比較抽象和難以理解。

那么,在本文中我們將會通過實踐的方式讓你能更快速的理解它們的概念和用法,所以與許多傳統(tǒng)干巴巴的教程都不同,我們將通過以下四個示例開始:

  • 示例1:用生日解釋Promise的基礎(chǔ)知識

  • 示例2:一個猜數(shù)字的游戲

  • 示例3:從Web API中獲取國家信息

  • 示例4:從Web API中獲取一個國家的周邊國家列表

示例1:用生日解釋Promise基礎(chǔ)知識

首先,我們先來看看Promise的基本形態(tài)是什么樣的。

Promise執(zhí)行時分三個狀態(tài):pending(執(zhí)行中)、fulfilled(成功)、rejected(失?。?。

new Promise(function(resolve, reject) {
    if (/* 異步操作成功 */) {
        resolve(value); //將Promise的狀態(tài)由padding改為fulfilled
    } else {
        reject(error); //將Promise的狀態(tài)由padding改為rejected
    }
})
實現(xiàn)時有三個原型方法then、catch、finally
promise
.then((result) => {
    //promise被接收或拒絕繼續(xù)執(zhí)行的情況
})
.catch((error) => {
    //promise被拒絕的情況
})
.finally (() => {
    //promise完成時,無論如何都會執(zhí)行的情況
})

基本形態(tài)介紹完成了,那么我們下面開始看看下面的示例吧。

用戶故事:我的朋友Kayo答應(yīng)在兩周后在我的生日Party上為我做一個蛋糕。

如果一切順利且Kayo沒有生病的話,我們就會獲得一定數(shù)量的蛋糕,但如果Kayo生病了,我們就沒有蛋糕了。但不論有沒有蛋糕,我們?nèi)匀粫_一個生日Party。

所以對于這個示例,我們將如上的背景故事翻譯成JS代碼,首先讓我們先創(chuàng)建一個返回Promise的函數(shù)。

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

在JavaScript中,我們可以使用new Promise()創(chuàng)建一個新的Promise,它接受一個參數(shù)為:(resolve,reject)=>{} 的函數(shù)。

在此函數(shù)中,resolve和reject是默認(rèn)提供的回調(diào)函數(shù)。讓我們仔細(xì)看看上面的代碼。

當(dāng)我們運行onMyBirthday函數(shù)2000ms后。

  • 如果Kayo沒有生病,那么我們就以2為參數(shù)執(zhí)行resolve函數(shù)

  • 如果Kayo生病了,那么我們用new Error("I am sad")作為參數(shù)執(zhí)行reject。盡管您可以將任何要拒絕的內(nèi)容作為參數(shù)傳遞,但建議將其傳遞給Error對象。

現(xiàn)在,因為onMyBirthday()返回的是一個Promise,我們可以訪問then、catch和finally方法。我們還可以訪問早些時候在then和catch中使用傳遞給resolve和reject的參數(shù)。

讓我們通過如下代碼來理解概念

如果Kayo沒有生病

onMyBirthday(false)
  .then((result) => {
    console.log(`I have ${result} cakes`); // 控制臺打印“I have 2 cakes” 
  })
  .catch((error) => {
    console.log(error); // 不執(zhí)行
  })
  .finally(() => {
    console.log("Party"); // 控制臺打印“Party”
  });

如果Kayo生病

onMyBirthday(true)
  .then((result) => {
    console.log(`I have ${result} cakes`); // 不執(zhí)行 
  })
  .catch((error) => {
    console.log(error); // 控制臺打印“我很難過”
  })
  .finally(() => {
    console.log("Party"); // 控制臺打印“Party”
  });

相信通過這個例子你能了解Promise的基本概念。

下面我們開始示例2

示例2:一個猜數(shù)字的游戲

基本需求:

  • 用戶可以輸入任意數(shù)字

  • 系統(tǒng)從1到6中隨機生成一個數(shù)字

  • 如果用戶輸入數(shù)字等于系統(tǒng)隨機數(shù),則給用戶2分

  • 如果用戶輸入數(shù)字與系統(tǒng)隨機數(shù)相差1,給用戶1分,否則,給用戶0分

  • 用戶想玩多久就玩多久

對于上面的需求,我們首先創(chuàng)建一個enterNumber函數(shù)并返回一個Promise:

const enterNumber = () => {
  return new Promise((resolve, reject) => {
    // 從這開始編碼
  });
};

我們要做的第一件事是向用戶索要一個數(shù)字,并在1到6之間隨機選擇一個數(shù)字:

const enterNumber = () => {
  return new Promise((resolve, reject) => {
    const userNumber = Number(window.prompt("Enter a number (1 - 6):")); // 向用戶索要一個數(shù)字
    const randomNumber = Math.floor(Math.random() * 6 + 1); // 選擇一個從1到6的隨機數(shù)
  });
};

當(dāng)用戶輸入一個不是數(shù)字的值。這種情況下,我們調(diào)用reject函數(shù),并拋出錯誤:

const enterNumber = () => {
  return new Promise((resolve, reject) => {
    const userNumber = Number(window.prompt("Enter a number (1 - 6):")); // 向用戶索要一個數(shù)字
    const randomNumber = Math.floor(Math.random() * 6 + 1); //選擇一個從1到6的隨機數(shù)

    if (isNaN(userNumber)) {
      reject(new Error("Wrong Input Type")); // 當(dāng)用戶輸入的值非數(shù)字,拋出異常并調(diào)用reject函數(shù)
    }
  });
};

下面,我們需要檢查userNumber是否等于RanomNumber,如果相等,我們給用戶2分,然后我們可以執(zhí)行resolve函數(shù)來傳遞一個object { points: 2, randomNumber } 對象。

如果userNumber與randomNumber相差1,那么我們給用戶1分。否則,我們給用戶0分。

return new Promise((resolve, reject) => {
  const userNumber = Number(window.prompt("Enter a number (1 - 6):")); // 向用戶索要一個數(shù)字
  const randomNumber = Math.floor(Math.random() * 6 + 1); // 選擇一個從1到6的隨機數(shù)

  if (isNaN(userNumber)) {
    reject(new Error("Wrong Input Type")); // 當(dāng)用戶輸入的值非數(shù)字,拋出異常并調(diào)用reject函數(shù)
  }

  if (userNumber === randomNumber) {
    // 如果相等,我們給用戶2分
    resolve({
      points: 2,
      randomNumber,
    });
  } else if (
    userNumber === randomNumber - 1 ||
    userNumber === randomNumber + 1
  ) {
    // 如果userNumber與randomNumber相差1,那么我們給用戶1分
    resolve({
      points: 1,
      randomNumber,
    });
  } else {
    // 否則用戶得0分
    resolve({
      points: 0,
      randomNumber,
    });
  }
});

下面,讓我們再創(chuàng)建一個函數(shù)來詢問用戶是否想繼續(xù)游戲:

const continueGame = () => {
  return new Promise((resolve) => {
    if (window.confirm("Do you want to continue?")) { // 向用戶詢問是否要繼續(xù)游戲
      resolve(true);
    } else {
      resolve(false);
    }
  });
};

為了不使游戲強制結(jié)束,我們創(chuàng)建的Promise沒有使用Reject回調(diào)。

下面,我們創(chuàng)建一個函數(shù)來處理猜數(shù)字邏輯:

const handleGuess = () => {
  enterNumber() // 返回一個Promise對象
    .then((result) => {
      alert(`Dice: ${result.randomNumber}: you got ${result.points} points`); // 當(dāng)resolve運行時,我們得到用戶得分和隨機數(shù) 
      
      // 向用戶詢問是否要繼續(xù)游戲
      continueGame().then((result) => {
        if (result) {
          handleGuess(); // If yes, 游戲繼續(xù)
        } else {
          alert("Game ends"); // If no, 彈出游戲結(jié)束框
        }
      });
    })
    .catch((error) => alert(error));
};

handleGuess(); // 執(zhí)行handleGuess 函數(shù)

在這當(dāng)我們調(diào)用handleGuess函數(shù)時,enterNumber()返回一個Promise對象。

如果Promise狀態(tài)為resolved,我們就調(diào)用then方法,向用戶告知競猜結(jié)果與得分,并向用戶詢問是否要繼續(xù)游戲。

如果Promise狀態(tài)為rejected,我們將顯示一條用戶輸入錯誤的信息。

不過,這樣的代碼雖然能解決問題,但讀起來還是有點困難。讓我們后面將使用async/await 對hanldeGuess進(jìn)行重構(gòu)。

網(wǎng)上對于 async/await 的解釋已經(jīng)很多了,在這我想用一個簡單概括的說法來解釋:async/await就是可以把復(fù)雜難懂的異步代碼變成類同步語法的語法糖

下面開始看重構(gòu)后代碼吧:

const handleGuess = async () => {
  try {
    const result = await enterNumber(); // 代替then方法,我們只需將await放在promise前,就可以直接獲得結(jié)果

    alert(`Dice: ${result.randomNumber}: you got ${result.points} points`);

    const isContinuing = await continueGame();

    if (isContinuing) {
      handleGuess();
    } else {
      alert("Game ends");
    }
  } catch (error) { // catch 方法可以由try, catch函數(shù)來替代
    alert(error);
  }
};

通過在函數(shù)前使用async關(guān)鍵字,我們創(chuàng)建了一個異步函數(shù),在函數(shù)內(nèi)的使用方法較之前有如下不同:

  • 和then函數(shù)不同,我們只需將await關(guān)鍵字放在Promise前,就可以直接獲得結(jié)果。

  • 我們可以使用try, catch語法來代替promise中的catch方法。

下面是我們重構(gòu)后的完整代碼,供參考:  

const enterNumber = () => {
  return new Promise((resolve, reject) => {
    const userNumber = Number(window.prompt("Enter a number (1 - 6):")); // 向用戶索要一個數(shù)字
    const randomNumber = Math.floor(Math.random() * 6 + 1); // 系統(tǒng)隨機選取一個1-6的數(shù)字

    if (isNaN(userNumber)) {
      reject(new Error("Wrong Input Type")); // 如果用戶輸入非數(shù)字拋出錯誤
    }

    if (userNumber === randomNumber) { // 如果用戶猜數(shù)字正確,給用戶2分
      resolve({
        points: 2,
        randomNumber,
      });
    } else if (
      userNumber === randomNumber - 1 ||
      userNumber === randomNumber + 1
    ) { // 如果userNumber與randomNumber相差1,那么我們給用戶1分
      resolve({
        points: 1,
        randomNumber,
      });
    } else { // 不正確,得0分
      resolve({
        points: 0,
        randomNumber,
      });
    }
  });
};

const continueGame = () => {
  return new Promise((resolve) => {
    if (window.confirm("Do you want to continue?")) { // 向用戶詢問是否要繼續(xù)游戲
      resolve(true);
    } else {
      resolve(false);
    }
  });
};

const handleGuess = async () => {
  try {
    const result = await enterNumber(); // await替代了then函數(shù)

    alert(`Dice: ${result.randomNumber}: you got ${result.points} points`);

    const isContinuing = await continueGame();

    if (isContinuing) {
      handleGuess();
    } else {
      alert("Game ends");
    }
  } catch (error) { // catch 方法可以由try, catch函數(shù)來替代
    alert(error);
  }
};

handleGuess(); // 執(zhí)行handleGuess 函數(shù)

我們已經(jīng)完成了第二個示例,接下來讓我們開始看看第三個示例。

示例3:從Web API中獲取國家信息

一般當(dāng)從API中獲取數(shù)據(jù)時,開發(fā)人員會精彩使用Promises。如果在新窗口打開https://restcountries.eu/rest/v2/alpha/cn,你會看到JSON格式的國家數(shù)據(jù)。

通過使用Fetch API,我們可以很輕松的獲得數(shù)據(jù),以下是代碼:

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

現(xiàn)在我們獲得了所需的國家/地區(qū)數(shù)據(jù),讓我們轉(zhuǎn)到最后一項任務(wù)。

示例4:從Web API中獲取一個國家的周邊國家列表

下面的fetchCountry函數(shù)從示例3中的api獲得國家信息,其中的參數(shù)alpha3Code 是代指該國家的國家代碼,以下是代碼

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

下面讓我們創(chuàng)建一個fetchCountryAndNeighbors函數(shù),通過傳遞cn作為alpha3code來獲取中國的信息。

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

  console.log(china);
};

fetchCountryAndNeighbors();

在控制臺中,我們看看對象內(nèi)容:  

JavaScript中Promise和Async/Await怎么用

在對象中,有一個border屬性,它是中國周邊鄰國的alpha3codes列表。

現(xiàn)在,如果我們嘗試通過以下方式獲取鄰國信息。

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

neighbors是一個Promise對象的數(shù)組。

當(dāng)處理一個數(shù)組的Promise時,我們需要使用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();

在控制臺中,我們應(yīng)該能夠看到國家/地區(qū)對象列表。

JavaScript中Promise和Async/Await怎么用

以下是示例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);
  }
};

const fetchCountryAndNeigbors = async () => {
  const china = await fetchCountry("cn");
  const neighbors = await Promise.all(
    china.borders.map((border) => fetchCountry(border))
  );
  console.log(neighbors);
};

fetchCountryAndNeigbors();

以上是“JavaScript中Promise和Async/Await怎么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI