异步函数的策略(async/await)

190 阅读1分钟

异步函数策略

1、实现sleep

`async` function sleep(delay) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, delay);
  });
}

async function test() {
  const t0 = Date.now();
  await sleep(1000);
  console.log(Date.now() - t0);
}

test();
// 1000

2、利用平行执行

如果使用await时不留心,则很可能错过平行加速的机会

async function randomDelay(id) {
  const delay = Math.random() * 1000;
  return new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${id} finished`);
      resolve();
    }, delay)
  );
}

async function foo() {
  const t0 = Date.now();
  await randomDelay(0);
  await randomDelay(1);
  await randomDelay(2);
  await randomDelay(3);
  await randomDelay(4);
  console.log(`${Date.now() - t0}ms elapsed`);
}

foo();
// 0 finished
// 1 finished
// 2 finished
// 3 finished
// 4 finished
// 3795ms elapsed

用for循环重写

async function randomDelay(id) {
  const delay = Math.random() * 1000;
  return new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${id} finished`);
      resolve();
    }, delay)
  );
}

async function foo() {
  const t1 = Date.now();
  for (let i = 0; i < 5; i++) {
    await randomDelay(i);
  }
  console.log(`${Date.now() - t1}ms elapsed`);
}

foo();
// 0 finished
// 1 finished
// 2 finished
// 3 finished
// 4 finished
// 3462ms elapsed

就算这些期约之间没有依赖,异步函数也会依次暂停,等待每个超时完成。这样可以保证执行顺序,但总执行时间会变长

如果顺序不是必须保证的,那么可以先一次性初始化所有期约,然后再分别等他们的结果,比如

async function randomDelay(id) {
  const delay = Math.random() * 1000;
  return new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${id} finished`);
      resolve();
    }, delay)
  );
}

async function foo() {
  const t1 = Date.now();
  const p0 = randomDelay(0);
  const p1 = randomDelay(1);
  const p2 = randomDelay(2);
  const p3 = randomDelay(3);
  const p4 = randomDelay(4);

  await p0;
  await p1;
  await p2;
  await p3;
  await p4;

  setTimeout(console.log, 0, `${Date.now() - t1}ms elapsed`);
}

foo();
// 0 finished
// 2 finished
// 3 finished
// 1 finished
// 4 finished
// 993ms elapsed

用数组和for循环再包装一下就是

async function randomDelay(id) {
  const delay = Math.random() * 1000;
  return new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${id} finished`);
      resolve();
    }, delay)
  );
}

async function foo() {
  const t0 = Date.now();

  const promises = Array(5)
    .fill(null)
    .map((_, index) => randomDelay(index));

  for (const p of promises) {
    await p;
  }

  console.log(`${Date.now() - t0}ms elapsed`);
}

foo();
// 2 finished
// 1 finished
// 4 finished
// 3 finished
// 0 finished
// 964ms elapsed

注意,虽然期约没有按照顺序执行,但await按照顺序收到了每个期约的值

async function randomDelay(id) {
  const delay = Math.random() * 1000;
  return new Promise((resolve) =>
    setTimeout(() => {
      console.log(`${id} finished`);
      resolve(id);
    }, delay)
  );
}

async function foo() {
  const t0 = Date.now();

  const promises = Array(5)
    .fill(null)
    .map((_, index) => randomDelay(index));

  for (const p of promises) {
    console.log(`awaiting ${await p}`);
  }

  console.log(`${Date.now() - t0}ms elapsed`);
}

foo();
// 3 finished
// 2 finished
// 4 finished
// 1 finished
// 0 finished
// awaiting 0
// awaiting 1
// awaiting 2
// awaiting 3
// awaiting 4
// 923ms elapsed

3、串行执行期约

function addTwo(x) {
  return x + 2;
}
function addThree(x) {
  return x + 3;
}
function addFour(x) {
  return x + 4;
}

async function addTen(x) {
  for (const fn of [addTwo, addThree, addFour]) {
    x = await fn(x);
  }
  return x;
}

addTen(10).then(console.log); // 19