温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何理解JavaScript Promise

发布时间:2021-09-30 10:03:45 来源:亿速云 阅读:138 作者:柒染 栏目:编程语言

如何理解JavaScript Promise,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

一般在开发中,查询网络API操作时往往是比较耗时的,这意味着可能需要一段时间的等待才能获得响应。因此,为了避免程序在请求时无响应的情况,异步编程就成为了开发人员的一项基本技能。

在JavaScript中处理异步操作时,通常我们经常会听到 "Promise "这个概念。但要理解它的工作原理及使用方法可能会比较抽象和难以理解。

示例1:用生日解释Promise基础知识

首先,我们先来看看Promise的基本形态是什么样的。

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完成时,无论如何都会执行的情况

})

基本形态介绍完成了,那么我们下面开始看看下面的示例吧。

用户故事:我的朋友Kayo答应在两周后在我的生日Party上为我做一个蛋糕。

如果一切顺利且Kayo没有生病的话,我们就会获得一定数量的蛋糕,但如果Kayo生病了,我们就没有蛋糕了。但不论有没有蛋糕,我们仍然会开一个生日Party。

所以对于这个示例,我们将如上的背景故事翻译成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后。

  • 如果Kayo没有生病,那么我们就以2为参数执行resolve函数

  • 如果Kayo生病了,那么我们用new Error("I am sad")作为参数执行reject。尽管您可以将任何要拒绝的内容作为参数传递,但建议将其传递给Error对象。

现在,因为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

示例2:一个猜数字的游戏

基本需求:

  • 用户可以输入任意数字

  • 系统从1到6中随机生成一个数字

  • 如果用户输入数字等于系统随机数,则给用户2分

  • 如果用户输入数字与系统随机数相差1,给用户1分,否则,给用户0分

  • 用户想玩多久就玩多久

对于上面的需求,我们首先创建一个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关键字,我们创建了一个异步函数,在函数内的使用方法较之前有如下不同:

  • 和then函数不同,我们只需将await关键字放在Promise前,就可以直接获得结果。

  • 我们可以使用try, catch语法来代替promise中的catch方法。

下面是我们重构后的完整代码,供参考:  

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 函数

我们已经完成了第二个示例,接下来让我们开始看看第三个示例。

示例3:从Web API中获取国家信息

一般当从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();

现在我们获得了所需的国家/地区数据,让我们转到最后一项任务。

示例4:从Web API中获取一个国家的周边国家列表

下面的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();

在控制台中,我们看看对象内容:  

如何理解JavaScript Promise

在对象中,有一个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();

在控制台中,我们应该能够看到国家/地区对象列表。

如何理解JavaScript Promise

以下是示例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在处理异步操作或不是同时发生的事情时很有用。

关于如何理解JavaScript Promise问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI