A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小刀葛小伦 于 2019-5-16 14:36 编辑

1.JS引擎
js 引擎是用来解释和执行js代码。
2.js引擎的执行机制
1.js是单线程语言
2.js需要异步
如果JavaScript中不存在异步, 只能自上而下执行, 如果上一行解析时间很长, 那么下面的代码就会被阻塞.对于用户而言, 阻塞就意味着 "卡死", 这样就导致了很差的用户体验.所以, JavaScript中存在异步执行
3.js如何实现异步
EventLoop(任务队列/事件循环)
- 1.所有的同步任务都在主线程上执行形成一个执行栈(stack);
- 2.主线程之外还存在一个任务队列(Event Loop),异步任务在event table中注册函数,当满足触发条件(即dom、ajax、setTimeout、setImmediate有返回结果了)后,被推入任务队列(Event Loop)。一旦主线程上的同步任务执行完成开始,系统就会开始读取任务队列(Event Loop), 看看里面有哪些对应得异步任务,于是结束等待状态,进入执行栈,开始执行。
- 3.主线程不断执行上面的第3步
console.log(1)
setTimeout(
        function() {
                console.log(2)
        },
        0)
console.log(3)
运行的结果是:1,3,2
代码分析:
1.console.log(1)是同步任务,放入主线程里
2.setTimeout是异步任务,被放入event table,0秒之后被推入任务队列(Event Loop)里
3.console.log(3)是同步任务,放到主线程里.
当1,3在控制台被打印后,主线程去Event Loop(事件队列)里查看是否有可执行的函数,执行setTimeout里的函数,这就是Event Loop

Event Loop
主线程从任务队列(Event Loop) 中读取事件, 这个过程是循环不断的, 所以整个的这种运行机制又称为Event Loop(事件循环).

除了广义的定义同步任务和异步任务还有:
1.macro-task(宏任务):包括整体代码script,setTimeout,setInterval,setImmediate
2.micro-task(微任务):Promise,process.nextTick,Object.observe(已废弃)、 MutationObserver

1. 宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。
2. 微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。
3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。听起来有点绕,我们用一段代码说明
console.log('1');

setTimeout(function() {
    console.log('2');
    process.nextTick(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
})
process.nextTick(function() {
    console.log('6');
})
new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
    process.nextTick(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

执行过程
整体script作为第一个宏任务进入主线程,遇到console.log,输出1。
遇到setTimeout,其回调函数被分发到宏任务Event Queue中。我们暂且记为setTimeout1。
遇到process.nextTick(),其回调函数被分发到微任务Event Queue中。我们记为process1。
遇到Promise,new Promise直接执行,输出7。then被分发到微任务Event Queue中。我们记为then1。
又遇到了setTimeout,其回调函数被分发到宏任务Event Queue中,我们记为setTimeout2。
宏任务Event Queue 微任务Event Queue
setTimeout1 process1
setTimeout2 then1

上表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1和7。
我们发现了process1和then1两个微任务。
执行process1,输出6。
执行then1,输出8。
好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。

那么第二轮时间循环从setTimeout1宏任务开始:
首先输出2。接下来遇到了process.nextTick(),同样将其分发到微任务Event Queue中,记为process2。new Promise立即执行输出4,then也分发到微任务Event Queue中,记为then2。
宏任务Event Queue 微任务Event Queue
setTimeout2 process2
then2

第二轮事件循环宏任务结束,我们发现有process2和then2两个微任务可以执行。
输出3。
输出5。
第二轮事件循环结束,第二轮输出2,4,3,5。

第三轮事件循环开始,此时只剩setTimeout2了,执行。
直接输出9。
将process.nextTick()分发到微任务Event Queue中。记为process3。
直接执行new Promise,输出11。
将then分发到微任务Event Queue中,记为then3。
宏任务Event Queue 微任务Event Queue
process3
then3

第三轮事件循环宏任务执行结束,执行两个微任务process3和then3。
输出10。
输出12。
第三轮事件循环结束,第三轮输出9,11,10,12。

整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。==(请注意,node环境下的事件监听依赖libuv与前端环境不完全相同,输出顺序可能会有误差)



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马