pwstrick / daily

一份搜集的前端面试题目清单、面试相关以及各类学习的资料(不局限于前端)
2.38k stars 242 forks source link

async/await的原理是什么? #980

Open pwstrick opened 4 years ago

pwstrick commented 4 years ago
console.log("script start");

async function async1() {
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2 end");
}
async1();

setTimeout(function () {
  console.log("setTimeout");
}, 0);

new Promise((resolve) => {
  console.log("Promise");
  resolve();
})
  .then(function () {
    console.log("promise1");
  })
  .then(function () {
    console.log("promise2");
  });

console.log("script end");

输出结果是怎么样的?

pwstrick commented 4 years ago

async/await其实就是一个基于 Promise 对象的简单自动执行器:

function run(gen){
  var g = gen();
  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }
  next();
}
function* foo() {
    let response1 = yield fetch('https://xxx')    //返回promise对象
    console.log('response1')
    console.log(response1)
}

生成器可以暂停代码的执行,只有显式地调用next()方法才能继续执行。 那么只要在异步完成后调用next()方法,就能实现暂停的效果,并且代码看着还是同步的。

参考《async/await 原理及执行顺序分析

pwstrick commented 4 years ago

ES7 引入了 async/await,这是 JavaScript 异步编程的一个重大改进,提供了在不阻塞主线程的情况下用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。 async/await 技术背后的秘密就是 Promise 和生成器应用,往低层说就是微任务和协程应用。

1)生成器 生成器(Generator)函数是一个带星号函数,而且可以暂停和恢复执行。

2)协程 协程是一种比线程更加轻量级的存在。 可以把协程看成是跑在线程上的任务,一个线程上可以存在多个协程,但是在线程上同时只能执行一个协程。 如果从 A 协程启动 B 协程,就把 A 协程称为 B 协程的父协程。 协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

//foo函数
function* foo() {
    let response1 = yield fetch('https://www.geekbang.org')
    console.log('response1')
    console.log(response1)
    let response2 = yield fetch('https://www.geekbang.org/test')
    console.log('response2')
    console.log(response2)
}

//执行foo函数的代码
let gen = foo()
function getGenPromise(gen) {
    return gen.next().value
}
getGenPromise(gen).then((response) => {
    console.log('response1')
    console.log(response)
    return getGenPromise(gen)
}).then((response) => {
    console.log('response2')
    console.log(response)
})