1.介绍

  • 它是使用了Generator函数基于Promise的封装,是Promise的一个语法糖;
  • 它是一种异步编程的解决方案,可以以同步的代码方式来写异步;
  • await关键字可以“暂停”async function的执行;
  • 可以用try-catch捕获到async function所得到的错误;

    2.基本使用

    声明两个promise对象:

    1. const promise1 = new Promise((resolve, reject) => {
    2. setTimeout(() => {
    3. resolve(1)
    4. }, 500)
    5. })
    6. const promise2 = new Promise((resolve, reject) => {
    7. setTimeout(() => {
    8. reject(2)
    9. }, 500)
    10. })

    传统的方式:

    1. promise1.then(res => {
    2. console.log(res); // 1
    3. })
    4. promise2.catch(res => {
    5. console.log(res); // 2
    6. })

    async await方式:

    1. async function asyncFunc(){
    2. const res = await promise1;
    3. console.log(res); // 1
    4. try{
    5. const res = await promise2;
    6. } catch(err) {
    7. console.log(err); // 2
    8. }
    9. }
    10. asyncFunc();

    3.进阶使用

    场景:要做三件事,下一件事依赖上一件事返回的结果;
    假设:dosomething返回的是Promise;
    处理顺序:dosomething1 => dosomething2 => dosomething3;

    1. // 传统的方式 (可以看到存在多重嵌套,错误处理也需要单独写)
    2. dosomething1().then(res1 => {
    3. dosomething2(res1).then(res2 => {
    4. dosomething3(res2).then(res3 => {
    5. console.log(res3)
    6. })
    7. })
    8. })
    9. // async await方式 (没了嵌套,简洁了)
    10. try {
    11. const res1 = await dosomething1();
    12. const res2 = await dosomething1(res1);
    13. const res3 = await dosomething1(res2);
    14. console.log(res3)
    15. } catch (error) {
    16. console.log(error) // 统一捕获错误
    17. }

    注意,如果每件事没有相互之间的依赖,使用了上面的那种方式后,会增加得到结果的时间(明明可以并行处理的,但是变成了串行);可以考虑使用Promise.all来执行:

    1. const [res1, res2, res3] = await Promise.all([dosomething1(), dosomething2(), dosomething3()])

    4.原理解读

    Generator函数

    Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,通过next()方法可以切换到下一个状态,可以控制代码执行流程(暂停和继续),从而为异步编程提供解决方案。
    基本使用:

    1. function* myGenerator() {
    2. yield '1'
    3. yield '2'
    4. return '3' // 到return这步,done为true;
    5. }
    6. const gen = myGenerator(); // 获取迭代器
    7. gen.next() //{value: "1", done: false}
    8. gen.next() //{value: "2", done: false}
    9. gen.next() //{value: "3", done: true}

    可以通过给next()传参, 让yield具有返回值:

    1. function* myGenerator() {
    2. console.log(yield '1') //res1
    3. console.log(yield '2') //res2
    4. console.log(yield '3') //res3
    5. }
    6. // 获取迭代器
    7. const gen = myGenerator();
    8. gen.next()
    9. gen.next('res1')
    10. gen.next('res2')
    11. gen.next('res3')

    await async的规范

  • async函数会自动返回一个Promise对象;

  • await关键字能够返回Promise的resolve的值;
  • await关键字必须用作async函数内;

    实现思路

  • 相同:可以看到 */yield和async/await这两个关键词有点类似的;

  • 不同:await每一步可以自动执行,不需要手动调用next()就能自动执行下一步;
  • 不同:asnyc返回的是一个Promise, Generator函数返回的是一个迭代器对象;

所以我们需要封装一个返回Promise对象的并且可以自动执行的Generator函数的函数:

  1. //通过包一层runAsync函数 模拟await async
  2. function* asyncFn() {
  3. try {
  4. var a = yield 1; // next()返回 { value: 1, done: false }
  5. var b = yield Promise.resolve(++a); // next()返回 { value: Promise.resolve(++a), done: false }
  6. //throw Error();
  7. var c = yield Promise.resolve(++b); // next()返回 { value: Promise.resolve(++b), done: false }
  8. console.log(c); // 3
  9. return c; // next()返回 { value: 3, done: false }
  10. } catch (error) {
  11. console.log(error,"error")
  12. }
  13. }
  14. function runAsync(asyncFn) {
  15. let g = new asyncFn();
  16. // async返回的是一个Promise
  17. return new Promise((resolve, reject) => {
  18. // 实现自动执行的方法
  19. function _next(val) {
  20. try {
  21. var res = g.next(val); //得到Generator对象: {value: xxx, done: xxx}
  22. } catch (error) {
  23. // 防止yield执行过程成抛出错误
  24. reject(error);
  25. return;
  26. }
  27. // 执行到最后;退出自动执行
  28. if (res.done) return resolve(res.value);
  29. // 自动执行下一个yield
  30. // 包一层Promise是为了兼容yield后面不是跟Promise对象的情况;
  31. Promise.resolve(res.value).then(
  32. (data) => {
  33. _next(data);
  34. },
  35. (err) => {
  36. g.throw(err); // 给外面的try catch捕获
  37. }
  38. );
  39. }
  40. _next();
  41. });
  42. }
  43. // 执行asyncFn,模拟的async await函数执行流程;
  44. let p = runAsync(asyncFn);
  45. p.then(res => {
  46. console.log(res) // 3
  47. }).catch((err) => {
  48. console.log(err);
  49. });