异步模式的特点:
- 不会等待这个任务结束才去开始下一个任务
- 对于耗时操作开启过后就立即去执行下一个任务
- 后续逻辑一般会通过回调函数的形式定义
示例
console.log('global begin')
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
- 开始执行,js内部引擎会将代码全部代码加载到js调用栈中
- 在js调用栈中压入一个匿名调用(相当于把代码放入到一个匿名函数中去执行)依次执行每行代码
- 对于console.log这样的同步api, 和同步是一样的先压入调用栈, 再执行, 最后弹出栈==> 控制台打印出global begin
- 将第一个setTimeOut压入调用栈, 但是这个函数的内部是异步调用, 所以需要关心内部环境做了什么事情(为timer函数开启了一个倒计时器, 然后单独放到一边), 这个倒计时器是单独工作的,并不会对JS的单线程模式产生影响, 所以说从调用开始过后就开始倒数了. webApi中记录1.8s的延迟后要执行的函数
- 第二个setTimeOut, 先压入调用栈, 再执行, 最后弹出栈. webApi中记录1s
- console.log这样的同步api, 和同步是一样的先压入调用栈, 再执行, 最后弹出栈==> 控制台打印出 global end
- 到此为止,<调用栈已全部清空, Event loop发挥作用(负责监听调用栈和消息队列)
- 当调用栈中的任务全部结束时, Event loop就会从消息队列中取出第一个回调函数压入到调用栈,但是当现在,消息队列中还什么都没有, 所以代码执行到现在算是先暂停下来
- 一秒钟之后, web Api中的延迟为1s的timer2会被放到消息队列的第一位 1.8s后, web Api中的延迟为1.8s的timer1会被放到消息队列的第二位
- 当消息队列发生变化时, Event loop就会将消息队列中的第一位timer2取出来压入调用栈中, 开启了新的执行==> 控制台打印出 timer2 invoke
- Event loop就会将消息队列中的timer1取出来压入调用栈中, 继续执行==> 控制台打印出 timer1 invoke , webApi中记录1s, 调用栈清空,
- 一秒钟之后, web Api中的延迟为1s的inner会被放到消息队列的第一位
- 消息队列发生变化, Event loop将消息队列中的第一位inner取出来压入调用栈中, 开启了新的执行==> 控制台打印出 inner invoke