小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
JavaScript编程
前言
众所周知,目前主流的JavaScript环境都是以单线程执行JavaScript代码,为啥采用单线程呢?自己百度不客气,欸就是玩儿
本章呢,主要带大家看看
- 同步模式与异步模式
- 事件循环与消息队列
来上车,兄弟们
·
怎么区别同步和异步模式呢?
用下面一个简单的例子给大家讲解一下:
- 同步:和同事一起去吃饭,同事说他有任务要做,做完了才能去吃饭,你等他做完了,一起去吃饭这就是
- 异步:和同事一起去吃饭,同事说他有任务要做,做完了才能去吃饭,你先去吃饭了,他做完了之后才去吃饭,互不干涉,这就是异步
同步模式(Synchronous)
同步模式就是代码中的任务依次执行,后一个代码需要等前一个代码执行完毕后在执行,执行顺序就是我们编程的时候的顺序。在单线程的情况下,我们大多数都会以同步执行(并非同时执行,而是排队执行)
例子
让我们看看下面的代码,默想一下会怎么执行?
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
}
foo()
console.log('global end')
ok,在我们执行这个代码的时候,首先,我们的调用栈Call statck中会被压入一个匿名调用,我们可以理解为我们全部的函数被放入一个匿名函数中进行执行
| Call statck | Console |
|---|---|
| (anonymous) | -- |
接着,我们就会逐行执行我们的代码,遇到第一个代码:console.log('global begin'),在执行完毕这行代码后,我们的控制台输出了对应的消息
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| console.log('global begin') | global begin |
执行完毕第一行代码后,我们的执行栈,会弹出console.log('global begin'),让我们的代码继续往下执行
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| global begin |
接下来我们遇到了两个函数的声明,function bar和function foo但是函数、变量的声明不会产生调用,所以我们继续往下,执行foo
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| global begin | |
| foo | foo task |
foo函数中调用了 bar函数
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| global begin | |
| foo | foo task |
| bar | bar task |
打印完成之后,bar函数就被任务栈弹出
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| global begin | |
| foo | foo task |
| bar task |
弹出bar后,我们的foo函数也调用结束了,他也被弹出任务栈,最后打印global end,我们的调用栈就会被清空
| Call statck | Console |
|---|---|
| (anonymous) | -- |
| global begin | |
| foo task | |
| bar task | |
| global end |
这就是简单的同步执行机制,但是有几个比较麻烦的问题,如果某一个执行出现了堵塞,就会出现堵死的情况,这就引入了新东西啦
异步模式
不会等待这个任务结束才开始下一个任务,开启过后就立即往后执行下一个任务,后续逻辑一般会通过回调函数的方式定义,异步模式对于JavaScript非常主要,如果没有这个模式及单线程的JavaScript就无法同事处理大量的耗时任务;那这里我们也引用一段代码,来描述一下异步执行的过程
console.log('begin')
setTimeout(() => {
console.log('timer1')
},1800)
setTimeout(() => {
console.log('timer2')
setTimeout(() => {
console.log('timer3')
},1000)
},1000)
console.log('end')
首先,我们的调用栈Call statck中会被压入一个匿名调用
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| -- | -- | -- | -- |
然后我们先执行第一行的console
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- |
接着就来到了第一个重头戏,setTimeout,因为这里是异步调用,所以我们需要关心内部的api调用都做了什么东西?其实内部的api也很简单,就是开启了1.8s的计时器
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | 1.8s timer1 | -- |
代码继续往下执行,压入新的栈
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | 1.8s timer1 | -- | |
| -- | 1s timer2 | -- |
最后遇到了console的调用
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | 1.8s timer1 | -- | |
| -- | 1s timer2 | -- | |
| end | -- | -- |
这里我们的调用栈就都被弹出了,就开始了Enven loop的执行
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | 1.8s timer1 | -- |
| -- | -- | 1s timer2 | -- |
两个异步任务根据时间情况,我们先执行timer2的内容,会在任务栈中压入一个方法
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | 1.8s timer1 | -- |
| -- | -- | -- | |
| 1s timer2 | -- | -- | -- |
接着我们会发现timer2中还有一个setTime
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | 1.8s timer1 | -- |
| -- | -- | -- | |
| timer2 | timer3 | -- |
然后在执行 timer1
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | -- | |
| -- | -- | -- | |
| timer2 | timer3 | -- | |
| timer1 | -- | -- |
再然后执行 timer3
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | -- | |
| -- | -- | -- | |
| timer2 | -- | ||
| timer1 | -- | -- | |
| -- | -- | -- | timer3 |
然后就是运行啦
| Call statck | Console | Web Apis | Queue |
|---|---|---|---|
| (anonymous) | -- | -- | -- |
| begin | -- | -- | |
| -- | -- | ||
| -- | -- | ||
| end | -- | -- | |
| -- | -- | -- | |
| -- | -- | -- | |
| timer2 | -- | ||
| timer1 | -- | -- | |
| -- | -- | -- | |
| timer3 | timer3 | -- |
大致就是这样啦,可能我说的比较绕,大家稍微理解理解哈