前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >20分钟带你掌握JavaScript Promise和 Async/Await

20分钟带你掌握JavaScript Promise和 Async/Await

作者头像
葡萄城控件
发布于 2020-12-14 07:16:21
发布于 2020-12-14 07:16:21
72000
代码可运行
举报
运行总次数:0
代码可运行

转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 原文出处:https://www.freecodecamp.org/news/learn-promise-async-await-in-20-minutes/

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

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

那么,在本文中我们将会通过实践的方式让你能更快速的理解它们的概念和用法,所以与许多传统干巴巴的教程都不同,我们将通过以下四个示例开始:

  • 示例1:用生日解释Promise的基础知识
  • 示例2:一个猜数字的游戏
  • 示例3:从Web API中获取国家信息
  • 示例4:从Web API中获取一个国家的周边国家列表

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

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

Promise执行时分三个状态:pending(执行中)、fulfilled(成功)、rejected(失败)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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没有生病

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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生病

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const enterNumber = () => {
  return new Promise((resolve, reject) => {
    // 从这开始编码
  });
};

我们要做的第一件事是向用户索要一个数字,并在1到6之间随机选择一个数字:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数,并抛出错误:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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分。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,
    });
  }
});

下面,让我们再创建一个函数来询问用户是否想继续游戏:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const continueGame = () => {
  return new Promise((resolve) => {
    if (window.confirm("Do you want to continue?")) { // 向用户询问是否要继续游戏
      resolve(true);
    } else {
      resolve(false);
    }
  });
};

为了不使游戏强制结束,我们创建的Promise没有使用Reject回调。

下面,我们创建一个函数来处理猜数字逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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就是可以把复杂难懂的异步代码变成类同步语法的语法糖

下面开始看重构后代码吧:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法。

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,我们可以很轻松的获得数据,以下是代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 是代指该国家的国家代码,以下是代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 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来获取中国的信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const fetchCountryAndNeighbors = async () => {
  const china= await fetchCountry("cn");

  console.log(china);
};

fetchCountryAndNeighbors();

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

在对象中,有一个border属性,它是中国周边邻国的alpha3codes列表。

现在,如果我们尝试通过以下方式获取邻国信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const neighbors = 
    china.borders.map((border) => fetchCountry(border));

neighbors是一个Promise对象的数组。

当处理一个数组的Promise时,我们需要使用Promise.all。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const fetchCountryAndNeigbors = async () => {
  const china = await fetchCountry("cn");

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

  console.log(neighbors);
};

fetchCountryAndNeigbors();

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

以下是示例4的所有代码,供您参考:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-12-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
JavaScript中的async/await
async 是一个修饰符,async 定义的函数会默认的返回一个Promise对象resolve的值,因此对async函数可以直接进行then操作,返回的值即为then方法的传入函数。
刘亦枫
2020/03/19
2K0
async/await实现Promise.all()
Promise.all() 方法接收一个 promise 的 iterable 类型(注:Array,Map,Set 都属于 ES6 的 iterable 类型)的输入,并且只返回一个Promise实例,并且输入的所有 promise 的 resolve 回调的结果是一个数组。
不叫猫先生
2023/11/23
1.6K0
深入理解 JavaScript 异步编程:Promise 与 Async/Await
作为一名 JavaScript 开发者,我经常需要处理 异步操作,比如访问 API、数据库查询或文件读取。起初,我曾被 回调地狱(Callback Hell) 困扰,代码难以维护,逻辑混乱,让人抓狂。直到我深入研究 Promise 和 Async/Await,终于摆脱了噩梦般的嵌套回调,代码风格变得更清晰、可读性更高。今天,我想通过这篇文章,帮你彻底搞懂 JavaScript 的异步编程。
Echo_Wish
2025/05/05
3000
深入理解 JavaScript 异步编程:Promise 与 Async/Await
手写async await的最简实现(20行)
如果让你手写async函数的实现,你是不是会觉得很复杂?这篇文章带你用20行搞定它的核心。
ssh_晨曦时梦见兮
2020/04/11
1.5K0
什么是Async await,和Promise有什么区别
总结:Async/awati 简单好用,是大势,肯定得学,而async的使用绕不开Promise,所以Promise是必会的。
子夜星辰
2022/11/15
1.5K0
JavaScript 异步解决方案 async/await
异步操作一直都是 JavaScript 中一个比较麻烦的事情,从最早的 callback hell,到TJ大神的 co,再到 Promise 对象,然后ES6中的 Generator 函数,每次都有所改进,但都不是那么彻底,而且理解起来总是很复杂。
李振
2021/11/26
4110
【温故知新】5 个 Promise 要避免的常见用法~
本篇译自:https://blog.bitsrc.io/5-common-mistakes-in-using-promises
掘金安东尼
2022/09/19
2410
javascript中优雅的处理async和await异常
无论是return了Promise, 还是内部调用了await, 就算什么也没做,它也返回promise; 一个函数,只要被标记了async,那么它就返回Promise对象 所以上面的handler函数返回的是一个promise实例
liulun
2019/09/29
8950
如何写出一个惊艳面试官的 Promise【近 1W字】 前言源码1.Promise2.Generator3.async 和 await4.Pro
1.高级 WEB 面试会让你手写一个Promise,Generator 的 PolyFill(一段代码); 2.在写之前我们简单回顾下他们的作用; 3.手写模块见PolyFill.
火狼1
2020/05/09
7230
如何写出一个惊艳面试官的 Promise【近 1W字】
                            前言源码1.Promise2.Generator3.async 和 await4.Pro
'return await promise' 与 'return promise' 这细微的区别
当从一个异步函数的promise返回时,我们可以使用return await promise等待 promise 解析完,也可以直接返回它 return promise。
前端小智@大迁世界
2022/05/31
1.1K0
JavaScript基础——深入学习async/await
大家好,上周我们一起学习了《JavaScript基础——Promise使用指南》, 明白了ES6增加的新特性——Promise让我们能够更加优雅的书写回调函数,清楚了Promise有哪些状态,以及如何编写Promise的相关代码。本篇文章,小编将和大家一起学习异步编程的未来——async/await,它会打破你对上篇文章Promise的认知,竟然异步代码还能这么写! 但是别太得意,你需要深入理解Promise后,才能更好的的驾驭async/await,因为async/await是基于Promise的,没有理解Promise,小编强烈建议各位再看看《JavaScript基础——Promise使用指南》。
前端达人
2018/12/17
1.9K2
JavaScript基础——深入学习async/await
Promise 和 Async/Await的区别
如果你正在阅读这篇文章,你可能已经理解了 promise 和 async/await 在执行上下文中的不同之处。
前端小智@大迁世界
2024/02/12
4140
Promise 和 Async/Await的区别
【ECMAScript6】es6 要点(二)Promise | 自个写一个Promise | Generator | Async/Await
但是,我们不能无限制地调用next从Generator实例中获取值。否则最后会返回undefined。原因:Generator犹如一种序列,一旦序列中的值被消费,你就不能再次消费它。即,序列为空后,再次调用就会返回undefined!。
前端修罗场
2023/10/07
3020
【ECMAScript6】es6 要点(二)Promise | 自个写一个Promise | Generator | Async/Await
async await 你真的用对了吗?
上述代码是一个典型,实际是从项目某个同学代码中抽象得来的。虽然看起来很工整很稳健,try catch做的很到位,但实际上,他没有把async和await理解透彻,没有理解到async返回的是Promise,无论是async内同步的报错还是异步(延迟)的报错,对上层调用来说,都是一个微任务。
用户1258909
2020/11/24
2.9K0
async await 你真的用对了吗?
Promise与Async/Await:异步编程的艺术
一个Promise对象代表了一个现在、将来或永远可能可用,也可能不可用的值。它有三种状态:pending(进行中)、fulfilled(已成功)或rejected(已失败)。
空白诗
2024/06/14
2150
Promise和async/await的总结
改用Promise后,调用更加灵活了,回调可以处理,也可以不处理,同时可以处理异步调用的异常,更具有通用性
陨石坠灭
2020/01/21
4700
JS中的 async 和 await 使用技巧
async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。 await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,它们会被自动转成立即 resolved 的 Promise 对象)。
青梅煮码
2023/02/18
1.9K0
JavaScript中Async/Await和Promise的区别
从Node 8 LTS开始,Node完全支持Async/Await。下面通过简单示例的方式来讲讲Async/Await和Promise的区别。 简单介绍下Async/Await:
Javanx
2019/09/04
2.8K0
JavaScript 的 async/await : async 和 await 在干什么
async 是“异步”的简写,而 await 可以认为是 async wait 的简写。 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。 await 只能出现在 async 函数中。
一个会写诗的程序员
2018/08/17
9930
JavaScript 的 async/await : async 和 await 在干什么
JavaScript异步编程:Promise、async&await与Generator
Promise 是 JavaScript 中用于处理异步操作的一种解决方案,它提供了一种更简洁、更清晰的方式来处理异步操作的结果。Promise 有三种状态:pending(进行中)、fulfilled(已完成,成功)和 rejected(已完成,失败)。Promise 的核心概念是链式调用,通过 .then() 方法处理成功(fulfilled)的结果,或者通过 .catch() 方法处理失败(rejected)的结果。
iwhao
2024/07/04
2981
推荐阅读
相关推荐
JavaScript中的async/await
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验