JS 之同步操作 vs 异步操作
共 3301字,需浏览 7分钟
·
2021-04-29 20:38
作者:Pingan8787
来源:SegmentFault 思否社区
1、单线程的JavaScript
我们都知道,js是一门单线程语言,何为单线程?就是在同一时间,只能做一件事。
为什么js要这么设计呢?js的主要用途就是操作DOM,与用户进行操作,所以如果js有两个线程,这时一个线程在某个节点上修改内容,另一个线程也在该节点上修改该内容,那js要以谁为准呢?
所以js的单线程当然是为了高效安全
为了提高利用多核CPU的计算能力,HTML5提出Web Worker标准,允许js脚本创建多个线程,但是子线程完全受主线程控制且不得操作DOM。所以这个新标准并没有改变js单线程的本质。
2、同步任务和异步任务
js的单线程就意味着所有任务都需要排队,前一个任务结束,才会执行下一个任务。但是IO设备很慢,当需要读取数据时,这时候CPU
就会停下来等待IO
操作,更要命的是即使该CPU
再忙,其它CPU
也不会帮忙,大家你看我我看你,这就特别影响用户体验了。
所以为了解决阻塞式IO
带来的不好的体验,js规定了,这时候主线程完全可以不管IO
设备,将其挂起处于等待中的任务,然后继续运行后面的任务,等到IO设备运行结果出来后,再回过头来,把挂起的任务继续执行下去。这就是异步操作。
于是,所有的任务可以分成两种,一种是同步任务,一种是异步任务
同步任务:在主线程上执行任务,这个前一个任务执行完之后,才能执行下一个任务;如果前一个任务没有执行完,那么线程会一直等待下去,直到该任务执行完才会继续执行 异步任务:任务不进入主线程,而是进入“消息队列”,主线程不会一直等待下去,而是继续执行下面的任务,当只有消息队列通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行
现在我们来看一下异步任务的执行机制:
所有同步任务都在主线程上执行,形成一个 执行栈
<img src="https://pic4.zhimg.com/80/v2-1778c62c1eaa529a5457eb6839547794_720w.png" alt="img" style="zoom:80%;" />
主线程之外,还存在一个 消息队列
,只要异步任务有了运行结果,就在消息队列
中放置一个事件,并通知主线程一旦 执行栈
中的所有同步任务执行完毕,系统就会读取消息队列
,相应的事件就结束了等待的状态,进入主线程,开始执行
主线程会不断的执行上面的三个步骤,只要主线程空了,就会去读取 消息队列
,这就是 JavaScript的执行机制。
3、消息队列和事件循环
消息队列就是队列,也是遵循先进先出的原则。IO
线程每完成一项任务,就会将该任务添加到消息队列中。
Event Loop
):事件循环是指主线程重复从消息队列中取消息、执行消息的过程。消息队列
中加入各种事件,只要栈中的代码执行完毕,主线程就会去读取 消息队列
,依次执行那些事件所对应的回调函数4、定时器
function callback() {
console.log('我是同步回调');
}
function bar(fn) {
console.log(123);
fn();
console.log(456);
}
bar(callback);
// 123
// 我是同步回调
// 456
callback
函数作为参数传给了bar
函数,在bar
函数中的callback
就是回调函数,而且是同步回调。function foo() {
console.log('我是异步回调');
}
function bar(fn) {
console.log(123);
setTimeout(fn, 1000);
console.log(456);
}
bar(foo);
// 123
// 456
// 我是异步回调
setTimeout
在bar
函数执行结束后延时1s后再执行,这种回调函数在主函数外部执行的过程就称为异步回调。setTimeout()
定时器是一个异步任务,系统会先执行执行栈中的同步任务,再回过头来执行 消息队列
中的事件。function foo() {
console.log('我是异步回调');
}
function bar(fn) {
console.log(123);
setTimeout(fn, 0);
console.log(456);
}
bar(foo);
// 123
// 456
// 我是异步回调
setTimeout
本质就是异步任务,无论如何它都会被挂起,js先执行同步任务后,发现消息队列中的任务可以执行了(setTimeout
延时时间到),就再去执行它。定时器事件虽然是添加到 任务队列
中了,但是也得等它定时完成之后,才会去指定它如果此时它已经位于队列的首位了,但是定时时间还未结束,此时,它也不会被执行,后面事件会先执行
第一种:把异步任务添加到消息队列尾部 第二种:把异步任务添加到微任务队列中,这样就可以在当前任务的末尾处执行微任务了