JavaScript运行机制,宏任务、微任务与Event Loop

一、JavaScript 代码运行的两个阶段:

1、预解析---把所有的函数定义提前,所有的变量声明提前,变量的赋值不提前。

2、执行---从上到下执行(按照js运行机制)。

二、JavaScript运行机制的特点:

1、JavaScript是一门单线程语言。

2、事件循环(Event Loop)。

三、JavaScript为什么是单线程?

  要回答这个问题,只要我们假设一下,如果JavaScript支持多线程,一个线程在某个DOM节点上添加内容,另外一个线程删除了这个节点,那么浏览器该以哪个线程为准呢?所以答案也就不言而喻了。为了避免复杂性,JavaScript从诞生就是单线程。
  在HTML5中,推出了web worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程的控制,且不得操作DOM,所以也是没有违背JavaScript单线程的本质。

四、任务队列

  因为JavaScript是单线程,意味着任务要一个接着一个完成,但是,如果前一个任务执行时间很长,那么后面的任务就得一直阻塞着,这样用户体验十分差。
  JavaScript的设计者考虑到了这一点,所以他将JavaScript的任务分为两种,在主线程上执行的任务"同步任务",被主线程挂载起来的任务"异步任务",后者一般是放在一个叫任务队列的数据结构中。
那么一般异步执行运行机制如下(也是JavaScript的运行机制):

  • (1)所有同步任务都在主线程上执行,形成一个执行栈。
  • (2)主线程之外,还有一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
  • (3)一旦“执行栈”中的所有同步任务执行完毕了,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  • (4)主线程不断重复上面的三步。(事件循环)

参考图片(图片来源 阮一峰老师讲解的JavaScript运行机制详解)

bg2014100801.jpg

 

再看一道面试题:

setTimeout(function(){
    console.log('1')
});

new Promise(function(resolve){
    console.log('2');
            resolve();
}).then(function(){
    console.log('3')
});

console.log('4');

首先说一下普通的异步函数的执行过程吧:

同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。

但是js异步有一个机制,就是遇到宏任务,先执行宏任务,将宏任务放入eventqueue,然后在执行微任务,将微任务放入eventqueue最骚的是,这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回掉函数,然后再从宏任务的queue上拿宏任务的回掉函数。 我当时看到这我就服了还有这种骚操作。

而宏任务一般是:包括整体代码script,setTimeout,setInterval。

微任务:Promise,process.nextTick。

记住就行了。

然后回到开头的代码。因为settimeout是宏任务,虽然先执行的他,但是他被放到了宏任务的eventqueue里面,然后代码继续往下检查看有没有微任务,检测到Promise的then函数把他放入了微任务序列。等到主线进程的所有代码执行结束后。先从微任务queue里拿回掉函数,然后微任务queue空了后再从宏任务的queue拿函数。

所以正确的执行结果当然是:2,4,3,1。

posted @ 2020-05-19 11:21  ZJTL  阅读(308)  评论(0编辑  收藏  举报