转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。
原文出处:https://www.freecodecamp.org/news/learn-promise-async-await-in-20-minutes/javascript
通常在开发中,查询网络API操做时每每是比较耗时的,这意味着可能须要一段时间的等待才能得到响应。所以,为了不程序在请求时无响应的状况,异步编程就成为了开发人员的一项基本技能。java
在JavaScript中处理异步操做时,一般咱们常常会听到 "Promise "这个概念。但要理解它的工做原理及使用方法可能会比较抽象和难以理解。编程
那么,在本文中咱们将会经过实践的方式让你能更快速的理解它们的概念和用法,因此与许多传统干巴巴的教程都不一样,咱们将经过如下四个示例开始:json
首先,咱们先来看看Promise的基本形态是什么样的。api
Promise执行时分三个状态:pending(执行中)、fulfilled(成功)、rejected(失败)。数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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完成时,不管如何都会执行的状况
})
|
基本形态介绍完成了,那么咱们下面开始看看下面的示例吧。promise
用户故事:个人朋友Kayo答应在两周后在个人生日Party上为我作一个蛋糕。网络
若是一切顺利且Kayo没有生病的话,咱们就会得到必定数量的蛋糕,但若是Kayo生病了,咱们就没有蛋糕了。但不论有没有蛋糕,咱们仍然会开一个生日Party。dom
因此对于这个示例,咱们将如上的背景故事翻译成JS代码,首先让咱们先建立一个返回Promise的函数。异步
1
2
3
4
5
6
7
8
9
10
11
|
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后。
如今,由于onMyBirthday()返回的是一个Promise,咱们能够访问then、catch和finally方法。咱们还能够访问早些时候在then和catch中使用传递给resolve和reject的参数。
让咱们经过以下代码来理解概念
若是Kayo没有生病
1
2
3
4
5
6
7
8
9
10
|
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生病
1
2
3
4
5
6
7
8
9
10
|
onMyBirthday(
true
)
.then((result) => {
console.log(`I have ${result} cakes`);
// 不执行
})
.
catch
((error) => {
console.log(error);
// 控制台打印“我很难过”
})
.finally(() => {
console.log(
"Party"
);
// 控制台打印“Party”
});
|
相信经过这个例子你能了解Promise的基本概念。
下面咱们开始示例2
基本需求:
对于上面的需求,咱们首先建立一个enterNumber函数并返回一个Promise:
1
2
3
4
5
|
const enterNumber = () => {
return
new
Promise((resolve, reject) => {
// 从这开始编码
});
};
|
咱们要作的第一件事是向用户索要一个数字,并在1到6之间随机选择一个数字:
1
2
3
4
5
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函数,并抛出错误:
1
2
3
4
5
6
7
8
9
10
|
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分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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,
});
}
});
|
下面,让咱们再建立一个函数来询问用户是否想继续游戏:
1
2
3
4
5
6
7
8
9
|
const continueGame = () => {
return
new
Promise((resolve) => {
if
(window.confirm(
"Do you want to continue?"
)) {
// 向用户询问是否要继续游戏
resolve(
true
);
}
else
{
resolve(
false
);
}
});
};
|
为了避免使游戏强制结束,咱们建立的Promise没有使用Reject回调。
下面,咱们建立一个函数来处理猜数字逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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就是能够把复杂难懂的异步代码变成类同步语法的语法糖。
下面开始看重构后代码吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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关键字,咱们建立了一个异步函数,在函数内的使用方法较以前有以下不一样:
下面是咱们重构后的完整代码,供参考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
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 函数
|
咱们已经完成了第二个示例,接下来让咱们开始看看第三个示例。
通常当从API中获取数据时,开发人员会精彩使用Promises。若是在新窗口打开https://restcountries.eu/rest/v2/alpha/cn,你会看到JSON格式的国家数据。
经过使用Fetch API,咱们能够很轻松的得到数据,如下是代码:
1
2
3
4
5
6
7
8
9
|
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();
|
如今咱们得到了所需的国家/地区数据,让咱们转到最后一项任务。
下面的fetchCountry函数从示例3中的api得到国家信息,其中的参数alpha3Code 是代指该国家的国家代码,如下是代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 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来获取中国的信息。
1
2
3
4
5
6
7
|
const fetchCountryAndNeighbors = async () => {
const china= await fetchCountry(
"cn"
);
console.log(china);
};
fetchCountryAndNeighbors();
|
在控制台中,咱们看看对象内容:
在对象中,有一个border属性,它是中国周边邻国的alpha3codes列表。
如今,若是咱们尝试经过如下方式获取邻国信息。
1
2
|
const neighbors =
china.borders.map((border) => fetchCountry(border));
|
neighbors是一个Promise对象的数组。
当处理一个数组的Promise时,咱们须要使用Promise.all。
1
2
3
4
5
6
7
8
9
10
11
|
const fetchCountryAndNeigbors = async () => {
const china = await fetchCountry(
"cn"
);
const neighbors = await Promise.all(
china.borders.map((border) => fetchCountry(border))
);
console.log(neighbors);
};
fetchCountryAndNeigbors();
|
在控制台中,咱们应该可以看到国家/地区对象列表。
如下是示例4的全部代码,供您参考:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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://gcdn.grapecity.com.cn/static/events/Promise-Async-Await-main.zip