文章目录
前言
JS是一门单线程语言,那就意味着一次只能执行一个任务且按顺序执行。如果有耗时任务,也必须等着它执行完了才能执行下一个任务。问题来了,浏览网页的时候,某个高清图片需要加载很久,那网页岂不是卡着等图片加载完才能做别的操作?
显然不是这样的,JS设计者设计了一种执行机制:事件循环机制(Event Loop),以实现单线程非阻塞的方法。
一、什么是事件循环机制?
通过上文,了解到事件循环机制(Event Loop)是用来实现单线程非阻塞的一种机制。
我们先了解下任务。任务可分为同步任务、异步任务,同步任务可以立即执行,一般会直接进入主线程中执行。异步任务指不进入主线程而进入任务队列的任务,一般是比较耗时的,如setTimeout、ajax请求等。异步任务再细分下,可分为宏任务、微任务:
- 宏任务(macro-task):包括整体代码script、setTimeout、setInterval、ajax、DOM事件
- 微任务(micro-task):Promise.then、Node 环境下的process.nextTick
然后,事件循环机制是如何执行任务的呢?进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。举例:
setTimeout(function() {
console.log('4');
})
new Promise(function(resolve) {
console.log('1');
resolve();
}).then(function() {
console.log('3');
})
console.log('2');
// 输出 1 2 3 4
- 这段代码作为宏任务,进入主线程。
- 遇到setTimeout,那么其回到函数注册后分发到宏任务Even Queue。
- 接下来是Promise,new Promise立即执行,then函数分发到微任务队列。
- 遇到console.log(),同步代码,立即执行。
- 此时第一个宏任务结束,看有哪些微任务?接下来执行微任务。
- 执行完微任务,第一次事件循环结束。接下来第二轮循环,从宏任务开始,执行宏任务队列setTimeout函数。
- 结束
流程图:
二、题目测验
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')
})
})
整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。
三、await会阻塞代码吗
function timer(){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('A')
},0)
console.log('B')
})
}
async function foo(){
let ans = await timer();
console.log(ans)
console.log('C');
}
foo()
console.log('D')
以上输出什么呢?
async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
首先是执行foo方法,foo方法里面的timer有await关键字修饰,timer需要等到promise执行完才能执行timer后面的代码。但是不会去等待timer里面的同步代码,所以先输出B。然后打印D。等到异步操作完成,再接着执行函数体内后面的语句,所以接着是A、C。
await
是generator
的语法糖,详细请看博客:深入理解ES6——迭代器与生成器
总结
1、JS是单线程语言,不管是什么新框架语法糖实现的所谓异步,其实,都是用同步方法模拟的。
2、事件循环是JS实现异步的方法,也是JS的执行机制。
3、执行和运行有很大的区别,javascript在不同的环境下,比如node,浏览器,Ringo等等,执行方式是不同的。而运行大多指javascript解析引擎,是统一的。