• JS高级 之 async - await


    目录

    一、先来个栗子 🌰

    0. 发送请求的函数

    1. 使用 回调函数

    2. 使用 Promise

    3. 使用 Generator

    01 - 普通

    02 - 优化,自动

    4. 使用 Async + Await

    二、异步函数 async

    1. 异步函数的写法

    写法一

    写法二

    写法三 

    写法四

    2.  异步函数的返回值

    01 -  返回普通值

    02 -  返回一个promise

    03 -  返回thenable对象

    3.  异步函数的异常

    01 - 使用Promise.reject

    02 - 抛出错误 

    三、异步函数中的 await

    1. await 的使用

    2. await 处理异步请求

    01 - resolve

    02 - reject

    3. await 后面的值

    01 - 普通

    02 - thenable对象

    03 - promise.resolve 

    04 - promise.reject

    4. await 和 async 结合


    一、先来个栗子 🌰

    首先来看个需求 : 

    向服务器发送网络请求获取数据,一共需要发送三次请求

    • 第一次请求
    • 第二次的请求url依赖于第一次的结果;
    • 第三次的请求url依赖于第二次的结果;
    • 依次类推

    0. 发送请求的函数

    1. // 模拟发送请求,真实情况肯定是不同的请求,这里只使用这一个,道理一样
    2. function requestHttp(url) {
    3. return new Promise((resolve) => {
    4. setTimeout(() => {
    5. resolve(url);
    6. }, 2000);
    7. });
    8. }

    1. 使用 回调函数

    1. // 第一次请求
    2. requestHttp('http://').then((res1) => {
    3. console.log('res1', res1); // res1 http://
    4. // 第二次请求
    5. requestHttp(res1 + 'www').then((res2) => {
    6. console.log('res2', res2); // res2 http://www
    7. // 第三次请求
    8. requestHttp(res2 + '.baidu.').then((res3) => {
    9. console.log('res3', res3); // res3 http://www.baidu.
    10. // 第四次请求
    11. requestHttp(res3 + 'com').then((res4) => {
    12. console.log('res4', res4); // res4 http://www.baidu.com
    13. });
    14. });
    15. });
    16. });

    2. 使用 Promise

    1. // 第一次请求
    2. requestHttp('http://')
    3. .then((res1) => {
    4. console.log('res1', res1); // res1 http://
    5. // 第二次请求
    6. return requestHttp(res1 + 'www');
    7. })
    8. .then((res2) => {
    9. console.log('res2', res2); // res2 http://www
    10. // 第三次请求
    11. return requestHttp(res2 + '.baidu.');
    12. })
    13. .then((res3) => {
    14. console.log('res3', res3); // res3 http://www.baidu.
    15. // 第四次请求
    16. return requestHttp(res3 + 'com');
    17. })
    18. .then((res4) => {
    19. console.log('res4', res4); // res4 http://www.baidu.com
    20. });

    3. 使用 Generator

    01 - 普通

    1. // 1. 定义生成器函数
    2. function* excuteCode() {
    3. const res1 = yield requestHttp('http://');
    4. console.log('res1', res1); // res1 http://
    5. const res2 = yield requestHttp(res1 + 'www');
    6. console.log('res2', res2); // res2 http://www
    7. const res3 = yield requestHttp(res2 + '.baidu.');
    8. console.log('res3', res3); // res3 http://www.baidu.
    9. const res4 = yield requestHttp(res3 + 'com');
    10. console.log('res4', res4); // res4 http://www.baidu.com
    11. }
    12. // 2. 拿到生成器对象
    13. const excuteIterator = excuteCode();
    14. // 3. 执行,把第一个请求的promise返回了出来,是promise
    15. excuteIterator.next().value.then((res1) => {
    16. // 4. 调用next()继续执行,同时把第一个请求的结果传入,把第二个请求的promise返回了出来
    17. excuteIterator.next(res1).value.then((res2) => {
    18. excuteIterator.next(res2).value.then((res3) => {
    19. excuteIterator.next(res3).value.then((res4) => {
    20. excuteIterator.next(res4);
    21. });
    22. });
    23. });
    24. });

    02 - 优化,自动

    1. // 1. 定义生成器函数
    2. function* excuteCode() {
    3. const res1 = yield requestHttp('http://');
    4. console.log('res1', res1); // res1 http://
    5. const res2 = yield requestHttp(res1 + 'www');
    6. console.log('res2', res2); // res2 http://www
    7. const res3 = yield requestHttp(res2 + '.baidu.');
    8. console.log('res3', res3); // res3 http://www.baidu.
    9. const res4 = yield requestHttp(res3 + 'com');
    10. console.log('res4', res4); // res4 http://www.baidu.com
    11. }
    12. // 2. 封装自动执行函数,把生成器函数传入
    13. function autoExcute(generatorFn) {
    14. // 3. 拿到生成器对象
    15. const excuteIterator = generatorFn();
    16. // 4. 定义递归函数,一次一次调用next方法
    17. function deepFn(url = '') {
    18. // { done: true/false, value: 值/undefined }
    19. const result = excuteIterator.next(url);
    20. // 执行完成时,done为true,直接返回
    21. if (result.done) {
    22. return;
    23. }
    24. // 没执行完,继续执行
    25. result.value.then((url) => {
    26. deepFn(url);
    27. });
    28. }
    29. // 5. 执行递归函数
    30. deepFn();
    31. }
    32. // 6. 开始执行函数
    33. autoExcute(excuteCode);

    4. 使用 Async + Await

    1. // 1. 定义执行的函数
    2. async function excuteCode() {
    3. // 2. 第一次请求
    4. const res1 = await requestHttp('http://');
    5. console.log('res1', res1); // res1 http://
    6. // 3. 第二次请求
    7. const res2 = await requestHttp(res1 + 'www');
    8. console.log('res2', res2); // res2 http://www
    9. // 3. 第三次请求
    10. const res3 = await requestHttp(res2 + '.baidu.');
    11. console.log('res3', res3); // res3 http://www.baidu.
    12. // 4. 第三次请求
    13. const res4 = await requestHttp(res3 + 'com');
    14. console.log('res4', res4); // res4 http://www.baidu.com
    15. }
    16. // 5. 执行代码
    17. excuteCode();

    二、异步函数 async

    async关键字用于声明一个异步函数

    async : 是asynchronous单词的缩写,异步、非同步

    sync : 是synchronous单词的缩写,同步、同时

    1. 异步函数的写法

    写法一

    1. async function foo() {}
    2. foo();

    写法二

    const bar = async function() {}
    

    写法三 

    const baz = async () => {}
    

    写法四

    1. class Person {
    2. async running() {}
    3. }

    2.  异步函数的返回值

    异步函数的返回值 : 一定是一个promise对象

    异步函数的返回值 : 一定是一个promise对象

    01 -  返回普通值

    异步函数的返回值相当于被包裹到Promise.resolve,相当于 => Promise.resolve( 普通值 )

    1. async function foo() {
    2. // 相当于return Promise.resolve([1])
    3. return [1];
    4. }
    5. foo().then((res) => {
    6. console.log(res);
    7. });

    02 -  返回一个promise

    如果返回值是Promise,状态由会由返回的Promise决定

    1. async function foo() {
    2. return new Promise((resolve) => {
    3. setTimeout(() => {
    4. resolve('冲冲冲');
    5. }, 3000);
    6. });
    7. }
    8. foo().then((res) => {
    9. console.log(res);
    10. });

    03 -  返回thenable对象

    返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定

    1. async function foo() {
    2. return {
    3. then(resolve) {
    4. setTimeout(() => {
    5. resolve('无敌');
    6. }, 5000);
    7. }
    8. };
    9. }
    10. foo().then((res) => {
    11. console.log(res);
    12. });

    3.  异步函数的异常

    如果在async中抛出了异常,那么程序它并不会像普通函数一样报错,这个异常不会被立即浏览器处理

    而是会作为Promise的reject来传递 => Promise.reject(error)

    01 - 使用Promise.reject

    1. async function foo() {
    2. console.log('111');
    3. // 1. 抛出异常
    4. return new Promise((_, reject) => {
    5. reject('err');
    6. });
    7. }
    8. foo()
    9. .then((res) => {
    10. console.log(res);
    11. })
    12. .catch((err) => {
    13. // 2. 这里可以捕获
    14. console.log(err);
    15. });

    02 - 抛出错误 

    1. async function foo() {
    2. console.log('111');
    3. // 1. 抛出异常
    4. throw new Error('我错了');
    5. console.log('222');
    6. }
    7. foo()
    8. .then((res) => {
    9. console.log(res);
    10. })
    11. .catch((err) => {
    12. // 2. 这里可以捕获
    13. console.log('oh', err);
    14. });

    三、异步函数中的 await

    1. await 的使用

    async函数可以在它内部使用await关键字,而普通函数中是不可以的

    await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码

    1. function bar() {
    2. console.log('bar function');
    3. return new Promise((resolve) => {
    4. setTimeout(() => {
    5. resolve(123);
    6. }, 2000);
    7. });
    8. }
    9. // await条件: 必须在异步函数中使用
    10. async function foo() {
    11. console.log('-------');
    12. // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
    13. const res1 = await bar();
    14. console.log('await后面的代码:', res1);
    15. const res2 = await bar();
    16. console.log('await后面的代码:', res2);
    17. console.log('-------');
    18. }
    19. foo();

    2. await 处理异步请求

    01 - resolve

    1. function requestData(url) {
    2. return new Promise((resolve, reject) => {
    3. setTimeout(() => {
    4. resolve(url)
    5. }, 2000);
    6. })
    7. }
    8. async function getData() {
    9. // 等待两秒,拿到url的值后才会继续执行
    10. const res1 = await requestData("coder")
    11. console.log("res1:", res1)
    12. // 这里同样等待两秒
    13. const res2 = await requestData(res1 + "star")
    14. console.log("res2:", res2)
    15. }

    02 - reject

    1. function requestData(url) {
    2. return new Promise((resolve, reject) => {
    3. setTimeout(() => {
    4. // 1. 如果发生报错
    5. reject('error message');
    6. }, 2000);
    7. });
    8. }
    9. // 2. 如果发生报错
    10. async function getData() {
    11. try {
    12. const res1 = await requestData('coder');
    13. console.log('res1:', res1);
    14. const res2 = await requestData(res1 + 'star');
    15. console.log('res2:', res2);
    16. } catch (error) {
    17. // 01 - 这里可以捕获
    18. console.log('err:', error);
    19. }
    20. }
    21. // 02 - 这里也可以捕获
    22. getData().catch((error) => {
    23. console.log('err:', err);
    24. });

    3. await后面的值

    通常await后面会跟上一个表达式 : 

    • 如果await后面是一个普通的值,那么会直接返回这个值
    • 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
    • 如果await后面的表达式,返回的Promise是resolve的状态,那么会将这个resolve的结果直接作为函数的Promise的resolve值
    • 如果await后面的表达式,返回的Promise是reject的状态,那么会将这个reject结果直接作为函数的Promise的reject值
    • 如果一直没有返回值,await就会一直等在那里,直到永远

    await会等,等待有个人把值给它!!!

    就像生成器函数中的,yield

    01 - 普通

    1. async function foo() {
    2. // 一般不这么写,和直接赋值没有区别
    3. const a = await 123;
    4. console.log(a); // 123
    5. }
    6. console.log('foo', foo()); // foo Promise { }

    02 - thenable对象

    1. async function foo() {
    2. // 等待两秒后拿到值
    3. const a = await {
    4. then(resolve) {
    5. setTimeout(() => {
    6. resolve(123);
    7. }, 2000);
    8. }
    9. };
    10. console.log(a); // 123
    11. }
    12. console.log('foo', foo()); // foo Promise { }

    03 - promise.resolve 

    1. async function foo() {
    2. // 等待两秒
    3. const a = await new Promise((resolve) => {
    4. setTimeout(() => {
    5. resolve(123);
    6. }, 2000);
    7. });
    8. console.log(a); // 123
    9. }
    10. console.log('foo', foo()); // foo Promise { }

    04 - promise.reject

    1. async function foo() {
    2. // 等待两秒
    3. const a = await new Promise((resolve, reject) => {
    4. setTimeout(() => {
    5. reject(123);
    6. }, 2000);
    7. });
    8. // 这里不会运行,直接一层一层往上抛错误,因为异步函数返回值为promise,所以可以在外面捕获
    9. console.log(a);
    10. }
    11. foo().catch((err) => {
    12. console.log(err);
    13. });

    4. await 和 async 结合

    因为异步函数的返回值一定是个promise,所以await会等待该promise的状态确定

    一旦确定下来,就会拿到相当于是promise.then中解析出的值

    1. // 1.普通函数返回promise对象
    2. function requestData(url) {
    3. console.log('request data');
    4. return {
    5. then(resolve) {
    6. setTimeout(() => {
    7. resolve(987);
    8. }, 2000);
    9. }
    10. };
    11. }
    12. // 2. 异步函数返回普通的值
    13. async function test() {
    14. console.log('test function');
    15. // 相当于返回的是 Promise.resolve('test')
    16. return 'test';
    17. }
    18. // 3. 异步函数|普通函数返回promise对象
    19. async function bar() {
    20. console.log('bar function');
    21. return new Promise((resolve) => {
    22. setTimeout(() => {
    23. resolve('bar');
    24. }, 2000);
    25. });
    26. }
    27. // 4. 异步函数|普通函数返回thenable对象
    28. async function demo() {
    29. console.log('demo function');
    30. return {
    31. then: function (resolve) {
    32. resolve('demo');
    33. }
    34. };
    35. }
    36. // 3.调用的入口async函数
    37. async function foo() {
    38. console.log('foo function');
    39. // 直接返回值
    40. const res1 = await requestData('coder');
    41. console.log('res1:', res1);
    42. // 直接返回值
    43. const res2 = await test();
    44. console.log('res2:', res2);
    45. // 会等待promise的状态确定后,才继续执行
    46. const res3 = await bar();
    47. console.log('res3:', res3);
    48. // 会等待thenable的状态确定后,才继续执行
    49. const res4 = await demo();
    50. console.log('res4:', res4);
    51. }
    52. // 执行
    53. foo();

  • 相关阅读:
    并查集快速合并(Java 实例代码)
    权限系统--前后端完全分离
    逻辑漏洞(基本概念、爆破)
    华为云云耀云服务器L实例使用教学 | 访问控制-安全组配置规则 实例教学
    免费数据 | 新库上线 | CnOpenData中国保险中介机构网点全集数据
    dp练习2
    21、学习MySQL 元数据
    APS车间排产软件实现企业生产数据可视化
    快速支持客户知识库的核心优势是什么?
    使用docker的常见bug
  • 原文地址:https://blog.csdn.net/a15297701931/article/details/126272491