文章目录

  • 一、概述
  • 二、同步模式/异步模式
  • 2.1 同步模式
  • 2.2 异步模式
  • 三、回调函数
  • 四、Promise (一种更优的异步编程统一方案)
  • 4.1 概述
  • 4.2 Promise 的基本用法
  • 4.3 Promise 使用案例
  • 4.4 Promise 常见误区
  • 4.5 Promise 链式调用
  • 4.6 Promise 异常处理
  • 4.7 Promise 静态方法
  • 4.7.1 Promise.resolve()
  • 4.7.2 Promise.reject()
  • 4.8 并行执行
  • 4.8.1 Promise.all()
  • 4.8.2 Promise.race()
  • 4.9 执行时序(宏任务/微任务)
  • 五、Generator 异步方案
  • 5.1 为什么要引入 Generator
  • 5.2 Generator 概念
  • 5.3 体验 Generator 函数异步方案
  • 5.4 递归 Generator 函数异步方案
  • 六、Async / Await 语法糖(ES8 规范新增)


next() 方法的返回值类似于迭代器,有一个 done 属性和一个 value 属性。函数体为空的生成器函数中间不会停留,调用一次 next() 就会让生成器到达 done: true 状态yield 关键字可以让生成器停止和开始执行,也是生成器最有用的地方,会让生成器到达 done: false 状态。生成器函数在遇到 yield 关键字之前会正常执行。遇到这个关键字后,执行会停止,函数作用域的状态会被保留。停止执行的生成器函数只能通过在生成器对象上调用 next() 方法来恢复执行function* foo() {

console.log("start");
  try {
    const res = yield "foo";
    console.log(res);
  } catch (e) {
    console.log(e);
  }
}
const generator = foo();
const result = generator.next();
console.log(result);
generator.next("bar"); // 如果传入一个参数,那么会作为 yield 左侧变量的返回值
generator.throw(new Error("Generator error"));

5.3 体验 Generator 函数异步方案

function ajax(url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.responseType = "json";
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    xhr.send();
  });
}

function* main() {
  const users = yield ajax("/api/users.json");
  console.log(users);
}
const g = main();
const result = g.next();
result.value.then((data) => {
  g.next(data);
});

5.4 递归 Generator 函数异步方案

function ajax(url) {
  return new Promise(function (resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.responseType = "json";
    xhr.onload = function () {
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    xhr.send();
  });
}

function* main() {
  try {
    const users = yield ajax("/api/users.json");
    console.log(users);
    const posts = yield ajax("/api/posts.json");
    console.log(posts);
    const urls = yield ajax("/api/urls.json");
    console.log(urls);
  } catch (e) {
    console.log(e);
  }
}

function co(generator) {
  const g = generator();
  function handleResult(result) {
    if (result.done) return; // 生成器函数结束
    result.value.then(
      (data) => {
        handleResult(g.next(data));
      },
      (error) => {
        g.throw(error);
      }
    );
  }
  handleResult(g.next());
}

co(main);

像这样的生成器函数在社区中早就有一个更完善的库,就叫做 co

六、Async / Await 语法糖(ES8 规范新增)

  • 相比于 Generator 最大的好处它不需要再配合一个类似 co 这样的执行器,因为他是语言层面的标准异步编程语法
  • async 关键字用于声明异步函数。这个关键字可以用在函数声明、函数表达式、箭头函数和方法上
  • Async 函数可以给我们返回一个 Promise 对象,这样更利于我们对整体代码进行控制
  • 使用 async 关键字可以让函数具有异步特征,但总体上其代码仍然是同步求值的。而在参数或闭包方面,异步函数仍然具有普通 JavaScript 函数的正常行为
  • 因为异步函数主要针对不会马上完成的任务,所以自然需要一种暂停和恢复执行的能力。使用 await 关键字可以暂停异步函数代码的执行,等待期约解决
  • 除此之外,还有一个点需要注意:async 中使用的 await 关键词,只能出现在 async 函数内部,它不能直接在外部也就是最顶层作用于使用

注意:await 关键字会暂停执行异步函数后面的代码,让出 JavaScript 运行时的执行线程。这个行为与生成器函数中的 yield 关键字是一样的

async function main () {
  try {
    const users = await ajax('/api/users.json')
    console.log(users)
    const posts = await ajax('/api/posts.json')
    console.log(posts)
    const urls = await ajax('/api/urls.json')
    console.log(urls)
  } catch (e) {
    console.log(e)
  }
}

// co(main)
const promise = main()

promise.then(() => {
  console.log('all completed')
})