关于js的事件循环

64 阅读2分钟

一、什么引发事件循环?

  • js是单线程的,但是当js想后端请求数据时,前端仍然可以操作按钮,这是因为浏览器除了js引擎还有其他的如Web APIs、GUI渲染进程。。。

二、规则

  • 同步和异步
    • 同步:从上到下逐步执行代码,前面的完成了后面才能继续
    • 同步任务:主线程上排队执行的任务,前一个完成,后面的才能继续
    • 异步:改变正常的程序运行流程
    • 异步任务:在任务队列中的任务,等待主线程任务完成才会按顺序执行的任务
  • 概述:同步任务放到执行栈中,异步任务放到执行队列中,先将执行栈中的任务执行完毕,再将执行队列中的任务按一定的顺序拿出来一个放到执行栈中去执行
  • 细分:程序按正常顺序执行,遇到同步任务就执行,遇到异步任务就按类型将他们分别放到微任务队列和宏任务队列中,直到同步任务执行结束,再去将微任务队列中的第一个任务拿到执行栈中执行,直到微任务队列中的任务执行结束,再去看宏任务队列,将宏任务队列中第一个任务拿到执行栈中执行,直到宏任务结束。

1. 执行栈

  • 同步任务按代码的执行顺序压入执行栈,栈顶的函数先执行,执行完毕后就出栈,接着去执行下一个

2. 执行队列

  • 异步任务按微任务宏任务分类,分别压入任务队列中
  • 执行队列中的任务又分为微任务队列宏任务队列,先微后宏
  • 微任务队列:promise、process.nextTick(node)、await后面的代码等
  • 宏任务队列:DOM操作、Ajax、setTimeout、setInterval等

3. 总结

  • 先栈(同步)后队列(异步),队列分微宏,微完就渲染,最后执行宏。

三、相关题目

1. 只有同步任务时

    function foo() {
      bar();
      console.log('foo');
    }

    function bar() {
      baz();
      console.log('bar');
    }

    function baz() {
      console.log('baz');
    }

    foo();
    /*
    baz
    bar
    foo
    */ 

2. 有微任务时

  • 使用new关键字创建的promise传递的函数会自动执行,.then里面的才是异步任务
    function foo() {
      console.log('foo');
    }
    console.log('global start');
    new Promise((resolve, reject) => {
      resolve();
      console.log('promise');
    }).then(() => {
      console.log('promise then');
    }).catch(() => {
      console.log('error');
    })
    foo();
    console.log('global end');
    /*
    global start
    promise
    foo
    global end
    promise then
    */

3. 有微任务&宏任务时

    function foo() {
      console.log('foo');
    }
    console.log('global start');
    setTimeout(() => {
      console.log('setTimeout: 0s');
    }, 0);
    new Promise(resolve => {
      console.log('promise');
      resolve();
    }).then(() => {
      console.log('promise then');
    })
    foo();
    console.log('global end');
    /*
    global start
    promise
    foo
    global end
    promise then
    setTimeout: 0s
    */

4. 有async&await时

  • promise async await
    async function async1() {
      console.log('async1 start');
      await async2();
      console.log('async1 end');
    }

    async function async2() {
      console.log('async2');
    }

    console.log('script start');

    setTimeout(() => {
      console.log('setTimeout');
    },0)

    async1();

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

    console.log('script end');
    
    /*
    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    setTimeout
    */