前言
问题
一般情况下,我们都认为js是顺序执行的
但是遇到下列情况
setTimeout(function(){
console.log('定时器开始啦')
});
new Promise(function(resolve){
console.log('马上执行for循环啦');
for(var i = 0; i < 10000; i++){
i == 99 && resolve();
}
}).then(function(){
console.log('执行then函数啦')
});
console.log('代码执行结束');
按照常规想法js执行输出
//"定时器开始啦"
//"马上执行for循环啦"
//"执行then函数啦"
//"代码执行结束"
//马上执行for循环啦
//代码执行结束
//执行then函数啦
//定时器开始啦
调用栈
- 每调用一个函数,解释器就会把该函数的执行上下文添加到调用栈并开始执行;
- 正在调用栈中执行的函数,如果还调用了其他函数,那么新函数也会被添加到调用栈,并立即执行;
- 当前函数执行完毕后,解释器会将其执行上下文清除调用栈,继续执行剩余执行上下文中的剩余代码;
- 但分配的调用栈空间被占满,会引发”堆栈溢出“的报错。
添加进栈中的代码会立即执行,执行完成后退出栈
同步任务 与 异步任务
同步任务发起调用后,很快就可以得到结果,
而异步任务是无法立即得到结果,
比如请求接口,每个接口都会有一定的响应时间,根据网速、服务器等等因素决定,再比如定时器,它需要固定时间后才会返回结果。
同步任务的执行,按照代码顺序和调用顺序,支持进入调用栈中并执行,执行结束后就移除调用栈。
而异步任务的执行,首先它依旧会进入调用栈中,然后发起调用,然后解释器会将其响应回调任务放入一个任务队列,紧接着调用栈会将这个任务移除。
当主线程清空后,即所有同步任务结束后,解释器会读取任务队列,并依次将已完成的异步任务加入调用栈中并执行。
事件循环
js是单线程的
事件循环是js实现异步的一种方法
js是单线程的,等前一个任务执行完才能执行下一个任务,代码只能一段一段执行
将一些耗时较长的任务处理为异步任务
因此产生了同步任务与异步任务的区别
当我们打开网站时,网页的渲染过程就是一大堆同步任务,比如页面骨架和页面元素的渲染。
而像加载图片音乐之类占用资源大耗时久的任务,就是异步任务。
- 同步和异步任务分别进入不同的执行"场所",同步的进入调用栈,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入Event Queue任务队列。
- 栈中的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
调用栈中的任务 --------- 任务队列中的任务
是什么
js执行过程中,遇到同步代码放入栈中立即执行,遇到异步代码对其进行一些操作之后放入任务队列中,
当栈中的代码都执行完毕,有一个机制会自动读取任务队列中的代码放入栈中执行,这个过程不断重复 即是事件循环
为什么
由于js是单线程的,代码只能一段一段执行,当遇到一些耗时比较长的操作时,页面会阻塞卡顿,影响用户体验,
而事件循环机制可同时处理多个异步操作,支持并发,减少阻塞