异步编程详解
同步任务、微任务、异步任务
const first = () =>
  new Promise((resolve, reject) => {
    console.log(3); // 同步1 3
    let p = new Promise((resolve, reject) => {
      console.log(7); // 同步2 7
      setTimeout(() => {
        console.log(5); // 异步1 5
        resolve(6);
        console.log(p); // 异步2 fulfilled 1
      }, 0);
      resolve(1);
    });
    resolve(2);
    p.then((arg) => {
      console.log(arg); // 微任务1 1
    });
  });
first().then((arg) => {
  console.log(arg); // 微任务2 2
});
console.log(4); // 同步3 4
在这段代码中,执行顺序如下:
- console.log(3)输出 3
- console.log(7)输出 7
- 设置一个 setTimeout,将console.log(5)和resolve(6)放入异步任务队列
- resolve(1)立即执行,随后- resolve(2)也立即执行
- console.log(4)输出 4
- 微任务队列中的 p.then先执行,输出 1
- 最后,first().then执行,输出 2
- 异步任务中的 console.log(5)输出 5,随后console.log(p)输出fulfilled
微任务
let a;
const b = new Promise((resolve, reject) => {
  console.log('promise1'); // 同步1 promise1
  resolve();
})
  .then(() => {
    console.log('promise2'); // 微1 promise2
  })
  .then(() => {
    console.log('promise3'); // 微2 promise3
  })
  .then(() => {
    console.log('promise4'); // 微3 promise4
  });
a = new Promise(async (resolve, reject) => {
  console.log(a); // 同步2 undefined
  console.log(b); // Promise fulfilled
  await b; // 等待b执行结束后,代码继续往下执行
  console.log(a); // 在等待b的时候 new Promise已经完成,promise pending
  console.log('after1'); // after1
  await a; // 等待a的处理结果,a 的结果在await之后,所以会一直等待
  resolve(true);
  console.log('after2');
});
console.log('end'); // 同步3 end
此代码展示了微任务的执行顺序:
- 输出 promise1
- 输出 undefined和Promise fulfilled
- 输出 end
- 微任务队列依次执行,输出 promise2、promise3、promise4
- await b完成后,输出- after1
- await a因为- a处于 pending 状态,后续代码暂停执行
Promise.all
function runAsync(x) {
  const p = new Promise((resolve) => {
    setTimeout(() => {
      resolve(x, console.log(x));
    }, 1000);
  });
  return p;
}
function runReject(x) {
  const p = new Promise((res, rej) => {
    setTimeout(() => {
      rej(`Error:${x}`, console.log(x));
    }, 1000 * x);
  });
  return p;
}
// 所有的Promise都成功了,res才会打印成功的值
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
  // 13 一起出现,因为时间一样,先2后4,2的错误先到,打印错误2
  .then((res) => console.log(res))
  .catch((err) => console.log(err));
Promise.all 会等待所有的 Promise 完成。如果有任意一个 Promise 失败,Promise.all 会立即进入 catch,输出第一个失败的错误。在此例中,runReject(2) 会在 2000ms 后先于 runReject(4) 报错,输出 Error:2。
Promise.race
function runAsync(x) {
  const p = new Promise((r) => {
    setTimeout(() => r(x, console.log(x)), 1000);
  });
  return p;
}
// 123一起执行,启动三个定时器,123都会打印,1最先返回成功
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  // res的结果,接收返回最快的值
  .then((res) => console.log('result:', res))
  .catch((err) => console.log(err));
Promise.race 会在第一个 Promise 完成或失败时立即返回结果。在此例中,runAsync(1) 最先完成,输出 1 并且 then 中的回调输出 result: 1。
async
async function async1() {
  // async没有await,剩下的代码都是同步代码
  console.log('1'); // 同步1
  // promise 的参数是个函数,这个函数是同步执行的
  new Promise((resolve) => {
    // 直接执行
    resolve();
    console.log('2'); // 同步 2
  });
  console.log('3'); // 同步3
}
async1();
console.log('4'); // 同步4
在没有 await 的情况下,async 函数中的代码同步执行,输出顺序为 1、2、3、4。
await
async function async1() {
  console.log('1'); // 同步1
  await async2(); // 等待 async2 执行完,包括里面的异步
  console.log('2'); // 微任务1
}
async function async2() {
  setTimeout(() => {
    console.log('3'); // 异步 1
  }, 0);
  console.log('4'); // 同步2
}
async1();
console.log(5); // 同步4
await 会暂停 async 函数的执行,等待 Promise 完成后继续。输出顺序为 1、4、5、2、3。
异步任务
async function async1() {
  console.log(1); // 同步1
  await async2(); // 等待async2()
  /* 微任务区域 */
  console.log(2); // async2了就执行
  setTimeout(() => {
    console.log(3); // 异步3
  }, 0);
  /* 微任务区域 */
}
async function async2() {
  // 最先注册的计时器
  setTimeout(() => {
    console.log(4); // 异步1
  }, 0);
  console.log(5); // 同步2
}
async1(); // 从这里开始执行
// async1();卡住了就继续执行当前宏任务
setTimeout(() => {
  console.log(6); // 异步2
}, 0);
console.log(7); // 同步3
执行顺序:
- 输出 1
- 输出 5
- 输出 7
- 微任务输出 2
- 异步任务按注册顺序输出 4、3、6
pending
async function async1() {
  console.log('1'); // 同步 2
  /* 这里的 Promise 是 pending 状态所以下面的代码不回执行 */
  await new Promise((resolve) => {
    console.log('2'); // 同步3 因为是在括号里的
  });
  /* ↓是微任务 */
  console.log('3');
  return '4';
}
console.log('5'); // 同步1
async1().then((res) => console.log(res));
console.log('6'); // 同步 4
由于 new Promise 中没有调用 resolve,Promise 始终处于 pending 状态,导致 await 后的代码不会执行。输出顺序为 5、1、2、6。
await 与微任务
async function async1() {
  console.log('1'); // 同步2
  await new Promise((resolve) => {
    console.log('2'); // 同步3
    resolve('3');
    // 继续按照代码顺序执行
  }).then((res) => console.log(res)); // 微1
  console.log('4'); // 微2
  return '5';
}
console.log('6'); // 同步1
async1().then((res) => console.log(res)); // 微3
console.log('7'); // 同步4
执行顺序:
- 输出 6
- 输出 1
- 输出 2
- 输出 7
- 微任务输出 3
- 微任务输出 4
- 微任务输出 5
微任务执行
async function async1() {
  console.log(1); // 同步2
  await new Promise((resolve) => {
    console.log(2); // 同步3
    resolve(3);
  });
  // 靠前面的微任务先执行
  console.log(4); // 微1
  return 5;
}
console.log(6); // 同步1
// 接收resolve(3)
async1().then((res) => {
  console.log(res); // 微2
});
new Promise((resolve) => {
  console.log(7); // 同步 4
  setTimeout(() => {
    console.log(8); // 异步1
  }, 0);
});
输出顺序:
- 输出 6
- 输出 1
- 输出 2
- 输出 7
- 微任务输出 4
- 微任务输出 5
- 异步任务输出 8
多个微任务
async function testSomething() {
  console.log('1'); // 同步 2
  return '2'; // 返回的是包装后的promise 属于微任务
}
async function testAsync() {
  console.log('3'); // 微任务 的同步任务1
  return Promise.resolve('4');
}
async function test() {
  console.log('5'); // 同步1
  // v1第一个先注册的微任务回调
  const v1 = await testSomething();
  console.log(v1); // 微1
  // v2 要等待 v1 的结果,v2 任务在v1后执行
  const v2 = await testAsync();
  console.log(v2); // 微3
  console.log(v1, v2); // 微4
}
test();
let promise = new Promise((resolve) => {
  console.log('6'); // 同步 3
  resolve('7'); // 微任务 2
});
// 第二个注册的微任务
promise.then((val) => console.log(val)); // 微2 结果
console.log('8'); // 同步 4
执行顺序:
- 输出 5
- 输出 1
- 输出 6
- 输出 3
- 输出 8
- 微任务输出 2
- 微任务输出 7
- 微任务输出 4
reject
async function async1() {
  await async2(); // 接收到reject,不继续执行
  console.log('1');
  return '2';
}
async function async2() {
  return new Promise((resolve, reject) => {
    console.log('3'); // 同步1
    reject('4');
  });
}
// 成功之后才会执行,所以会报错
async1()
  .then((res) => console.log(res))
  .catch((err) => {
    console.log(err); // 捕获错误4
  });
在 async2 中调用 reject,导致 async1 中的 await 捕获到错误,进入 catch,输出 4。
try...catch
async function async1() {
  try {
    await Promise.reject('1');
  } catch (e) {
    console.log(2); // 1 捕获错误继续执行
  }
  console.log(3); // 2
  return Promise.resolve('4');
}
async1().then((res) => console.log(res)); // 3
console.log('5'); // 同步1
通过 try...catch 捕获 Promise 的拒绝,代码继续执行。输出顺序为 5、2、3、4。
综合示例
const async1 = async () => {
  console.log('1'); // 同步2
  setTimeout(() => {
    console.log('2'); // 异步2
  }, 2000);
  await new Promise((resolve) => {
    console.log(3); // 同步3
  });
  // 微任务无状态,后续代码不执行
  console.log(4);
  return 5;
};
console.log('6'); // 同步1
async1().then((res) => console.log(res)); // 一直等待Promise的值
console.log(7); // 同步4
// Promise
Promise.resolve(1)
  // .then必须接收的是函数,忽略
  .then(2)
  // 忽略
  .then(Promise.resolve(3))
  // 忽略
  .catch(4)
  .then((res) => console.log(res)); // 微1 1
setTimeout(() => {
  console.log(8); // 异步1
}, 1000);
在这个综合示例中,展示了 async/await 与 Promise 的复杂交互。输出顺序为:
- 输出 6
- 输出 1
- 输出 3
- 输出 7
- 输出 1(来自Promise.resolve(1)的微任务)
- 输出 5
- 输出 8
- 输出 2(两秒后)
- 输出 4
finally
const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve(1);
    console.log(2); // 3 2
  }, 0);
  resolve(3);
  resolve(4);
})
  .then((res) => {
    console.log(res); // 1  3
    setTimeout(() => {
      console.log(p1); //4 Promise fulfilled
    }, 1000);
  })
  .finally((res) => {
    // 因为前一个.then没有resolved 或 rejected 返回 undefined
    console.log('finally', res); // 2 finally undefined
  });
finally 无论 Promise 最终状态如何,都会执行。在此例中,输出顺序为:
- 输出 3(resolve(3)优先于resolve(4))
- 输出 finally undefined
- 输出 1
- 输出 2
- 一秒后输出 Promise { <fulfilled>: 3 }