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. 发送请求的函数

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

1. 使用 回调函数

// 第一次请求
requestHttp('http://').then((res1) => {
  console.log('res1', res1); // res1 http://
  // 第二次请求
  requestHttp(res1 + 'www').then((res2) => {
    console.log('res2', res2); // res2 http://www
    // 第三次请求
    requestHttp(res2 + '.baidu.').then((res3) => {
      console.log('res3', res3); // res3 http://www.baidu.
      // 第四次请求
      requestHttp(res3 + 'com').then((res4) => {
        console.log('res4', res4); // res4 http://www.baidu.com
      });
    });
  });
});

2. 使用 Promise

// 第一次请求
requestHttp('http://')
  .then((res1) => {
    console.log('res1', res1); // res1 http://

    // 第二次请求
    return requestHttp(res1 + 'www');
  })
  .then((res2) => {
    console.log('res2', res2); // res2 http://www

    // 第三次请求
    return requestHttp(res2 + '.baidu.');
  })
  .then((res3) => {
    console.log('res3', res3); // res3 http://www.baidu.

    // 第四次请求
    return requestHttp(res3 + 'com');
  })
  .then((res4) => {
    console.log('res4', res4); // res4 http://www.baidu.com
  });

3. 使用 Generator

01 - 普通

// 1. 定义生成器函数
function* excuteCode() {
  const res1 = yield requestHttp('http://');
  console.log('res1', res1); // res1 http://

  const res2 = yield requestHttp(res1 + 'www');
  console.log('res2', res2); // res2 http://www

  const res3 = yield requestHttp(res2 + '.baidu.');
  console.log('res3', res3); // res3 http://www.baidu.

  const res4 = yield requestHttp(res3 + 'com');
  console.log('res4', res4); // res4 http://www.baidu.com
}

// 2. 拿到生成器对象
const excuteIterator = excuteCode();

// 3. 执行,把第一个请求的promise返回了出来,是promise
excuteIterator.next().value.then((res1) => {
  // 4. 调用next()继续执行,同时把第一个请求的结果传入,把第二个请求的promise返回了出来
  excuteIterator.next(res1).value.then((res2) => {
    excuteIterator.next(res2).value.then((res3) => {
      excuteIterator.next(res3).value.then((res4) => {
        excuteIterator.next(res4);
      });
    });
  });
});

02 - 优化,自动

// 1. 定义生成器函数
function* excuteCode() {
  const res1 = yield requestHttp('http://');
  console.log('res1', res1); // res1 http://

  const res2 = yield requestHttp(res1 + 'www');
  console.log('res2', res2); // res2 http://www

  const res3 = yield requestHttp(res2 + '.baidu.');
  console.log('res3', res3); // res3 http://www.baidu.

  const res4 = yield requestHttp(res3 + 'com');
  console.log('res4', res4); // res4 http://www.baidu.com
}

// 2. 封装自动执行函数,把生成器函数传入
function autoExcute(generatorFn) {
  // 3. 拿到生成器对象
  const excuteIterator = generatorFn();
  // 4. 定义递归函数,一次一次调用next方法
  function deepFn(url = '') {
    // { done: true/false, value: 值/undefined }
    const result = excuteIterator.next(url); 
    // 执行完成时,done为true,直接返回
    if (result.done) {
      return;
    }
    // 没执行完,继续执行
    result.value.then((url) => {
      deepFn(url);
    });
  }
  // 5. 执行递归函数
  deepFn();
}

// 6. 开始执行函数
autoExcute(excuteCode);

4. 使用 Async + Await

// 1. 定义执行的函数
async function excuteCode() {
  // 2. 第一次请求
  const res1 = await requestHttp('http://');
  console.log('res1', res1); // res1 http://

  // 3. 第二次请求
  const res2 = await requestHttp(res1 + 'www');
  console.log('res2', res2); // res2 http://www

  // 3. 第三次请求
  const res3 = await requestHttp(res2 + '.baidu.');
  console.log('res3', res3); // res3 http://www.baidu.

  // 4. 第三次请求
  const res4 = await requestHttp(res3 + 'com');
  console.log('res4', res4); // res4 http://www.baidu.com
}

// 5. 执行代码
excuteCode();

二、异步函数 async

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

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

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

1. 异步函数的写法

写法一

async function foo() {}

foo();

写法二

const bar = async function() {}

写法三 

const baz = async () => {}

写法四

class Person {
  async running() {}
}

2.  异步函数的返回值

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

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

01 -  返回普通值

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

async function foo() {
  // 相当于return Promise.resolve([1])
  return [1];
}

foo().then((res) => {
  console.log(res);
});

02 -  返回一个promise

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

async function foo() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('冲冲冲');
    }, 3000);
  });
}

foo().then((res) => {
  console.log(res);
});

03 -  返回thenable对象

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

async function foo() {
  return {
    then(resolve) {
      setTimeout(() => {
        resolve('无敌');
      }, 5000);
    }
  };
}

foo().then((res) => {
  console.log(res);
});

3.  异步函数的异常

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

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

01 - 使用Promise.reject

async function foo() {
  console.log('111');
  // 1. 抛出异常
  return new Promise((_, reject) => {
    reject('err');
  });
}

foo()
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    // 2. 这里可以捕获
    console.log(err);
  });

02 - 抛出错误 

async function foo() {
  console.log('111');
  // 1. 抛出异常
  throw new Error('我错了');
  console.log('222');
}

foo()
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    // 2. 这里可以捕获
    console.log('oh', err);
  });

三、异步函数中的 await

1. await 的使用

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

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

function bar() {
  console.log('bar function');
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(123);
    }, 2000);
  });
}

// await条件: 必须在异步函数中使用
async function foo() {
  console.log('-------');
  // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
  const res1 = await bar();
  console.log('await后面的代码:', res1);
  const res2 = await bar();
  console.log('await后面的代码:', res2);

  console.log('-------');
}
foo();

2. await 处理异步请求

01 - resolve

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 2000);
  })
}

async function getData() {
  // 等待两秒,拿到url的值后才会继续执行
  const res1 = await requestData("coder")
  console.log("res1:", res1)
  // 这里同样等待两秒
  const res2 = await requestData(res1 + "star")
  console.log("res2:", res2)
}

02 - reject

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 1. 如果发生报错
      reject('error message');
    }, 2000);
  });
}

// 2. 如果发生报错
async function getData() {
  try {
    const res1 = await requestData('coder');
    console.log('res1:', res1);
    const res2 = await requestData(res1 + 'star');
    console.log('res2:', res2);
  } catch (error) {
    // 01 - 这里可以捕获
    console.log('err:', error);
  }
}

// 02 - 这里也可以捕获
getData().catch((error) => {
  console.log('err:', err);
});

3. await后面的值

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

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

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

就像生成器函数中的,yield

01 - 普通

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

console.log('foo', foo()); // foo Promise { <pending> }

02 - thenable对象

async function foo() {
  // 等待两秒后拿到值
  const a = await {
    then(resolve) {
      setTimeout(() => {
        resolve(123);
      }, 2000);
    }
  };
  console.log(a); // 123
}

console.log('foo', foo()); // foo Promise { <pending> }

03 - promise.resolve 

async function foo() {
  // 等待两秒
  const a = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(123);
    }, 2000);
  });
  console.log(a); // 123
}

console.log('foo', foo()); // foo Promise { <pending> }

04 - promise.reject

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

foo().catch((err) => {
  console.log(err);
});

4. await 和 async 结合

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

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

// 1.普通函数返回promise对象
function requestData(url) {
  console.log('request data');
  return {
    then(resolve) {
      setTimeout(() => {
        resolve(987);
      }, 2000);
    }
  };
}
// 2. 异步函数返回普通的值
async function test() {
  console.log('test function');
  // 相当于返回的是 Promise.resolve('test')
  return 'test';
}

// 3. 异步函数|普通函数返回promise对象
async function bar() {
  console.log('bar function');
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('bar');
    }, 2000);
  });
}

// 4. 异步函数|普通函数返回thenable对象
async function demo() {
  console.log('demo function');
  return {
    then: function (resolve) {
      resolve('demo');
    }
  };
}

// 3.调用的入口async函数
async function foo() {
  console.log('foo function');
  // 直接返回值
  const res1 = await requestData('coder');
  console.log('res1:', res1);
  // 直接返回值
  const res2 = await test();
  console.log('res2:', res2);

  // 会等待promise的状态确定后,才继续执行
  const res3 = await bar();
  console.log('res3:', res3);

  // 会等待thenable的状态确定后,才继续执行
  const res4 = await demo();
  console.log('res4:', res4);
}
// 执行
foo();

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值