快速学习JavaScript中的Promise和Async/Await
一样在开发中,查询网络API操纵时往往是比力耗时的,这意味着大概需要一段时间的等候才能获得响应。因此,为了不程序在恳求时无响应的状况,异步编程就成为了开发人员的一项根本技艺。
在JavaScript中处置异步操纵时,平常我们经常会听到 "Promise "这个概念。但要懂得它的工作道理及使用办法大概会比力抽象和难以懂得。
相关引荐:《javascript视频教程》
那么,在本文中我们将会通过实践的方式让你能更快速的懂得它们的概念和用途,所以与很多传统干巴巴的教程都不一样,我们将通过以下四个示例开端:
- 示例1:用生日说明Promise的根基知识
- 示例2:一个猜数字的游戏
- 示例3:从Web API中猎取我国信息
- 示例4:从Web API中猎取一个我国的旁边我国列表
示例1:用生日说明Promise根基知识
第一,我们先来看看Promise的根本形状是啥样的。
Promise施行时分三个状态:pending(施行中)、fulfilled(成功)、rejected(失败)。
new Promise(function(resolve, reject) { if (/* 异步操纵成功 */) { resolve(value); //将Promise的状态由padding改为fulfilled } else { reject(error); //将Promise的状态由padding改为rejected } }) 实现时有三个原型办法then、catch、finally promise .then((result) => { //promise被接收或回绝连续施行的状况 }) .catch((error) => { //promise被回绝的状况 }) .finally (() => { //promise完成时,不管怎样都会施行的状况 })
根本形状介绍完成了,那么我们下面开端看看下面的示例吧。
会员故事:我的伴侣Kayo应允在两周后在我的生日Party上为我做一个蛋糕。
假如一切顺利且Kayo没有生病的话,我们就会获得必然数目的蛋糕,但假如Kayo生病了,我们就没有蛋糕了。但不管有没有蛋糕,我们依然会开一个生日Party。
所以关于这个示例,我们将如上的背景故事翻译成JS代码,第一让我们先创立一个返回Promise的函数。
const onMyBirthday = (isKayoSick) => { return new Promise((resolve, reject) => { setTimeout(() => { if (!isKayoSick) { resolve(2); } else { reject(new Error("I am sad")); } }, 2000); }); };
在JavaScript中,我们可以使用new Promise()创立一个新的Promise,它接受一个参数为:(resolve,reject)=>{} 的函数。
在此函数中,resolve和reject是默许供给的回调函数。让我们细心看看上面的代码。
当我们运转onMyBirthday函数2000ms后。
- 假如Kayo没有生病,那么我们就以2为参数施行resolve函数
- 假如Kayo生病了,那么我们用new Error("I am sad")作为参数施行reject。尽管您可以将任何要回绝的内容作为参数传递,但倡议将其传递给Error对象。
此刻,由于onMyBirthday()返回的是一个Promise,我们可以拜访then、catch和finally办法。我们还可以拜访早些时候在then和catch中使用传递给resolve和reject的参数。
让我们通过如下代码来懂得概念
假如Kayo没有生病
onMyBirthday(false) .then((result) => { console.log(`I have ${result} cakes`); // 操纵台打印“I have 2 cakes” }) .catch((error) => { console.log(error); // 不施行 }) .finally(() => { console.log("Party"); // 操纵台打印“Party” });
假如Kayo生病
onMyBirthday(true) .then((result) => { console.log(`I have ${result} cakes`); // 不施行 }) .catch((error) => { console.log(error); // 操纵台打印“我很难受” }) .finally(() => { console.log("Party"); // 操纵台打印“Party” });
信赖通过这个例子你能理解Promise的根本概念。
下面我们开端示例2
示例2:一个猜数字的游戏
根本需求:
- 会员可以输入任意数字
- 系统从1到6中随机生成一个数字
- 假如会员输入数字等于系统随机数,则给会员2分
- 假如会员输入数字与系统随机数相差1,给会员1分,不然,给会员0分
- 会员想玩多久就玩多久
关于上面的需求,我们第一创立一个enterNumber函数并返回一个Promise:
const enterNumber = () => { return new Promise((resolve, reject) => { // 从这开端编码 }); };
我们要做的第一件事是向会员索要一个数字,并在1到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); // 选中一个从1到6的随机数 }); };
当会员输入一个不是数字的值。这种状况下,我们调取reject函数,并抛出错误:
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); //选中一个从1到6的随机数 if (isNaN(userNumber)) { reject(new Error("Wrong Input Type")); // 当会员输入的值非数字,抛出非常并调取reject函数 } }); };
下面,我们需要检查userNumber可否等于RanomNumber,假如相等,我们给会员2分,然后我们可以施行resolve函数来传递一个object { points: 2, randomNumber } 对象。
假如userNumber与randomNumber相差1,那么我们给会员1分。不然,我们给会员0分。
return new Promise((resolve, reject) => { const userNumber = Number(window.prompt("Enter a number (1 - 6):")); // 向会员索要一个数字 const randomNumber = Math.floor(Math.random() * 6 + 1); // 选中一个从1到6的随机数 if (isNaN(userNumber)) { reject(new Error("Wrong Input Type")); // 当会员输入的值非数字,抛出非常并调取reject函数 } 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, }); } });
下面,让我们再创立一个函数来扣问会员可否想连续游戏:
const continueGame = () => { return new Promise((resolve) => { if (window.confirm("Do you want to continue?")) { // 向会员扣问可否要连续游戏 resolve(true); } else { resolve(false); } }); };
为了不使游戏强迫完毕,我们创立的Promise没有使用Reject回调。
下面,我们创立一个函数来处置猜数字逻辑:
const handleGuess = () => { enterNumber() // 返回一个Promise对象 .then((result) => { alert(`Dice: ${result.randomNumber}: you got ${result.points} points`); // 当resolve运转时,我们得到会员得分和随机数 // 向会员扣问可否要连续游戏 continueGame().then((result) => { if (result) { handleGuess(); // If yes, 游戏连续 } else { alert("Game ends"); // If no, 弹出游戏完毕框 } }); }) .catch((error) => alert(error)); }; handleGuess(); // 施行handleGuess 函数
在这当我们调取handleGuess函数时,enterNumber()返回一个Promise对象。
假如Promise状态为resolved,我们就调取then办法,向会员告知竞猜结果与得分,并向会员扣问可否要连续游戏。
假如Promise状态为rejected,我们将显示一条会员输入错误的信息。
不外,这样的代码虽然能解决问题,但读起来还是有点艰难。让我们后面将使用async/await 对hanldeGuess停止重构。
网上关于 async/await 的说明已经许多了,在这我想用一个简便概括的说法来说明:async/await就是可以把复杂难懂的异步代码变成类同步语法的语法糖。
下面开端看重构后代码吧:
const handleGuess = async () => { try { const result = await enterNumber(); // 代替then办法,我们只需将await放在promise前,就可以直接获得结果 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函数来替换 alert(error); } };
通过在函数前使用async关键字,我们创立了一个异步函数,在函数内的使用办法较此前有如下不一样:
- 和then函数不一样,我们只需将await关键字放在Promise前,就可以直接获得结果。
- 我们可以使用try, catch语法来代替promise中的catch办法。
下面是我们重构后的完全代码,供参照 :
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); // 系统随机拔取一个1-6的数字 if (isNaN(userNumber)) { reject(new Error("Wrong Input Type")); // 假如会员输入非数字抛出错误 } 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, }); } }); }; const continueGame = () => { return new Promise((resolve) => { if (window.confirm("Do you want to continue?")) { // 向会员扣问可否要连续游戏 resolve(true); } else { resolve(false); } }); }; const handleGuess = async () => { try { const result = await enterNumber(); // await替换了then函数 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函数来替换 alert(error); } }; handleGuess(); // 施行handleGuess 函数
我们已经完成了第二个示例,接下来让我们开端看看第三个示例。
示例3:从Web API中猎取我国信息
一样当从API中猎取数据时,开发人员会出色使用Promises。假如在新窗口翻开https://restcountries.eu/rest/v2/alpha/cn,你会看到JSON格局的我国数据。
通过使用Fetch API,我们可以很轻松的获得数据,以下是代码:
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();
此刻我们获得了所需的我国/地区数据,让我们转到最后一项任务。
示例4:从Web API中猎取一个我国的旁边我国列表
下面的fetchCountry函数从示例3中的api获得我国信息,其中的参数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); } };
下面让我们创立一个fetchCountryAndNeighbors函数,通过传递cn作为alpha3code来猎取我国的信息。
const fetchCountryAndNeighbors = async () => { const china= await fetchCountry("cn"); console.log(china); }; fetchCountryAndNeighbors();
在操纵台中,我们看看对象内容:
在对象中,有一个border属性,它是我国旁边邻国的alpha3codes列表。
此刻,假如我们尝试通过以下方式猎取邻国信息。
const neighbors = china.borders.map((border) => fetchCountry(border));
neighbors是一个Promise对象的数组。
当处置一个数组的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();
在操纵台中,我们应当能够看到我国/地区对象列表。
以下是示例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();
总结
完成这4个示例后,你可以看到Promise在处置异步操纵或不是同时发生的事情时很有用。信赖在不竭的实践中,对它的懂得会越深、越强,但愿这篇文章能对大家懂得Promise和Async/Await带来一些帮忙。
以下是本文中使用的代码:
https://files.cnblogs.com/files/powertoolsteam/Promise-Async-Await-main.zip
原文出处:https://www.freecodecamp.org/news/learn-promise-async-await-in-20-minutes/