前言:
深入浅出:本文尽量(不敢保证)以通俗易懂原则解释,避免大家陷入过于理论、晦涩,想了解一个问题,带出好几个问题的尴尬窘境。保证(斗胆保证)大家有所收获,如果没有收获,那么劳驾多看几遍,顺便动动你可爱的小手。学而不思则罔,思而不码则殆.废话不多说,开始正题。
JS执行机制:
知其然,知其所以然。众所周知,JS从创立之初就定性为单线程语言,如果想查经据典,请移步阮一峰的个人网站。代码执行自上而下,一行一行执行,如果有一行报错,那么后面代码将不能执行。先执行同步代码,再执行异步代码。异步基于回调来实现,event-loop就是异步实现的原理。
异步场景:
1.定时器:setTimemout/setInterval
2.事件监听:dom操作,事件驱动
3.Ajax:代理XMLHttpRequest发送请求,实现局部更新
4.Promises: ES6,语法糖,异步编程提供统一接口,链式写法,解决ajax回调地狱问题。
5.Asyn/Await:ES7,语法糖。
6.publish-subscribe pattern:发布/订阅者模式。思想前卫、用户体验更好。
目下先了解event-loop实现原理,其它Ajax、Promise、Async/Await、publish-subscribe pattern后续视情况更新。
Event-loop演示图
示例代码
示例1:
1: setTimeout(() => {
2: console.log('asynchronous2')
:3: }, 500);
4: console.log('synchronization1')
4: setTimeout(() => {
5: console.log('asynchronous1')
6: }, 100);
7: setTimeout(() => {
8: console.log('asynchronous3')
9: }, 100);
10: console.log('synchronization2')
执行结果:
1:打印:synchronization1、synchronization2,
2:打印:asynchronous1、asynchronous3、
3:打印:asynchronous2
示例2:
1: console.log('synchronization1')
2: const btn = document.getElementById('btn')
3: btn.addEventListener('click', function () {
4: console.log('dom:asynchronous')
5: })
6: console.log('synchronization2')
执行结果:
先打印:synchronization1、synchronization2
点击按钮打印:dom:asynchronous
思考
那么问题来了,示例1和示例2中代码到底是如何实现的?跟上图有什么关系。客官勿急,且看下文。
示例1:
1: 第一行同步代码,丢到call stack,执行打印"'synchronization1'”,并清空当前call stack
2: 第二行setTimeout异步代码,丢到web api,记录当前定时器时间及要执行代码。示例2中不同的是,则将点击事件记录在web api中
。
3: 第六行同步代码,丢到call stack,执行打印"synchronization2",并清空当前call stac
4: 当前调用栈为空,未有待执行任务。一旦同步代码执行完,此时启动event-loop轮询机制,查找callback quene是否有任务队列。有则捞出,丢到call stack里执行。那么callback quene的队列是怎么来的,涉及到web api中异步代码。根据定时器定义时间长短及代码顺讯,如示例代码依次将100ms/asynchronous1(执行顺讯在前,时间最短),100ms/asynchronous3(执行顺讯在后,时间最短)、3ms/asynchronous2(等待时间最长)
.web api 则将异步代码丢到evnent-loop.此时若call stack为空,继续event-loop轮询机制。示例2中代码不同的是当点击事件触发时,则将执行代码丢到callback quene中
。