text: // ----- 异步函数 -----
  // 某期约在 1000 毫秒后解决为数值3，若其他代码需访问它的值，应该增加期约处理程序
  /* let p = new Promise((resolve, reject) => setTimeout(resolve, 1000, 3));
  p.then((x) => console.log(x));
  
  // 也可以定义一个函数
  function handler(x) { console.log(x); }
  p.then(handler); */
  
  // async 关键字用于声明异步函数(函数声明，函数表达式，箭头函数和方法)
  // async function foo() {}
  // let bar = async function () {};
  // let baz = async () => {};
  // class Qux {
  //   async qux() {}
  // }
  // 注意，async 关键字可以让函数具有异步特征，但其代码仍同步求值
  // 不过，异步函数如果使用 return 关键字，这个值会被 Promise.resolve() 包装为一个期约对象
  
  // async 让函数具有异步特征，但其代码仍然同步求值
  // 如果有 return 则这个值会被 Promise.resolve() 包装成一个期约对象
  /* async function foo() {
    console.log(1);
    return 3;
  }
  foo().then(console.log);
  console.log(2); // 1, 2, 3 */
  
  // 如果异步函数返回的是 thenable 接口的对象，则这个对象提供给 then() 解包，如果不是则返回值被当作解决了的期约
  /* async function baz() {
    const thenable = {
      then(callback) { callback('baz'); },
    };
    return thenable;
  }
  baz().then(console.log); */
  
  // 拒绝期约的错误不会被异步函数捕获，抛出的错误可以
  /* async function foo() {
    console.log(1);
    throw Error(3);
  }
  foo().catch(console.log);
  console.log(2); */
  // 1
  // 2
  // Error: 3
  
  /* async function foo() {
    console.log(1);
    Promise.reject(3);
  }
  foo().catch(console.log);
  console.log(2); */
  // 1
  // 2
  // UnhandledPromiseRejection
  
  // --- await ---
  // 使用 await 暂停执行异步函数后面的代码，让出 JS 运行时的执行线程
  // await 同样会尝试解包对象的值，将值给表达式再异步恢复异步函数的执行
  // await 期待一个实现 thenable 接口的对象，并对这个对象进行解包
  
  // await 会抛出错误的同步操作，会返回拒绝的期约
  /* async function foo() {
    console.log(1);
    await (() => { throw Error(3); })();
    console.log(4)
  }
  foo().catch(console.log);
  console.log(2); */
  // 1
  // 2
  // Error: 3
  // 对拒绝的期约使用 await 会释放错误值 (将拒绝期约返回)
  
  /* async function foo() {
    console.log(1);
    await (async () => { await (() => { console.log('4'); })(); })();
    console.log(5);
  }
  foo().catch(console.log);
  console.log(2);
  // 1, 4, 2, 5 */
  
  /* async function foo() {
    console.log(1);
    await (async () => {
      await (async () => {}
      )();
      console.log('4');
    })();
    console.log(5);
  }
  foo().catch(console.log);
  console.log(2);
  // 1, 2, 4, 5 */
  
  /* async function foo() {
    console.log(1);
    (async () => {
      await (async () => {}
      )();
      console.log('4');
    })();
    console.log(5);
  }
  foo().catch(console.log);
  console.log(2);
  // 1, 5, 2, 4 */
  
  // await 必须在异步函数中使用，或者是立即调用异步函数(无法在外层运行环境直接 await )
  /* (async function () {
    console.log(await Promise.resolve(3));
  }()); // 3 */
  
  // JS 运行时遇到 await 会记录在哪里暂停，等 await 右边的值可用后向消息队列推送一个任务以恢复异步函数的执行
  // 即使 await 后面跟着一个立即可用的值，其余部分也会被异步求值
  /* async function foo() {
    console.log(2);
    await null;
    console.log(4);
  }
  
  console.log(1);
  foo();
  console.log(3); */
  // 1, 2, 3, 4
  
  // 若 await 后面是期约，则会有两个任务被添加到消息队列并被异步求值
  /* async function foo() {
    console.log(2);
    console.log(await Promise.resolve(6));
    console.log(7);
  }
  
  async function bar() {
    console.log(4);
    console.log(await 8);
    console.log(9);
  }
  
  console.log(1);
  foo();
  console.log(3);
  bar();
  console.log(5); */
  
  // 1. 利用异步实现 sleep
  /* async function sleep(delay) {
    return new Promise((resolve) => setTimeout(resolve, delay));
  }
  
  async function foo() {
    const t0 = Date.now();
    await sleep(1500);
    console.log(Date.now() - t0);
  }
  foo(); // 1509 */
  
  // 2. 利用平行执行
  /* async function randomDelay(id) {
    const delay = Math.random() * 1000;
    return new Promise((resolve) => setTimeout(() => {
      console.log(`${id} finished`);
      resolve(id);
    }, delay));
  } */
  
  // for 循环
  /* async function bar() {
    const t0 = Date.now();
    for (let i = 0; i < 5; i++) {
      await randomDelay(i);
    }
    console.log(`${Date.now() - t0}`);
  }
  bar(); */
  // 0 finished
  // 1 finished
  // 2 finished
  // 3 finished
  // 4 finished
  // 2961
  
  // 在不追求 finish 顺序时 Promise.all()
  /* async function foo() {
    const t0 = Date.now();
    const results = [];
    for (let i = 0; i < 5; i++) {
      results.push(randomDelay(i));
    }
    await Promise.all(results);
    console.log(`${Date.now() - t0}`);
  }
  foo(); */
  // 4 finished
  // 2 finished
  // 0 finished
  // 3 finished
  // 1 finished
  // 925
  
  // 2. 先初始化，期约没按顺序执行但 await 按顺序收到每个期约的值)
  // const p0 = randomDelay(0);
  // const p1 = randomDelay(1);
  // await p0;
  // await p1;
  
  /* async function foo() {
    const t0 = Date.now();
    const promises = Array(5).fill(null).map((_, i) => randomDelay(i));
    for (const p of promises) {
      await p; // await-in-loop ESLint 不建议，但通过 Promise.all() 无法保证输出顺序
      console.log(p);
    }
    console.log(`${Date.now() - t0}`);
  }
  foo(); */
  
  // 串行执行期约
  /* async function addTwo(x) { return x + 2; }
  async function addThree(x) { return x + 3; }
  async function addFive(x) { return x + 5; }
  
  async function addTen(x) {
    let num = x;
    for (const fn of [addTwo, addThree, addFive]) {
      num = await fn(num);
    }
    return num;
  }
  
  addTen(9).then(console.log); */
  
  const x = { num: 1 };
  async function add(countTimes = 0) {
    let y = { num: 0 };
    y.num ++;
    countTimes++;
    if (countTimes < 5) {
      console.log(countTimes);
      return add(countTimes);
    }
    x.num += y.num;
  }
  add();
  console.log(x);