一、JS执行机制:
重点有以下两点:
1.JavaScript是一门单线程语言。
2.Event Loop(事件循环)是JavaScript的执行机制。
既然说js是单线程,那就是在执行代码的时候是从上往下执行的,先来看一段代码:
setTimeout(function(){
console.log('定时器开始')
});
new Promise(function(resolve){
console.log('Promise开始');
resolve();
}).then(function(){
console.log('执行then函数')
});
console.log('代码执行结束');
输出结果2:
关于javascript
javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker, 但javascript是单线程这一核心扔未改变。所以一切javascript版的“多线程”都是用单线程模拟出来的,一切javascript多线程都是纸老虎
1、JS为什么是单线程的
最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突的操作,那么浏览器的解析器是无法执行的。
2、js为什么需要异步
如果js中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。 对于用户而言,阻塞就以为着“卡死”,这样就导致了很差的用户体验。比如在进行ajax请求的时候如果没有返回数据后面的代码就没办法执行。
3、js单线程又是如何实现异步的呢
js中的异步以及多线程都可以理解成为一种“假象”,就拿h5的WebWorker来说,子线程有诸多限制,不能控制DOM,不能修改全局对象等等,通常只用来做计算做数据处理。
这些限制并没有违背我们之前的观点,所以说是“假象”。JS异步的执行机制其实就是事件循环(eventloop),理解了eventloop机制,就理解了js异步的执行机制。
4、JS的事件循环(eventloop)是怎么运作的
事件循环、eventloop\运行机制 这三个术语其实说的是同一个东西,
“先执行同步操作异步操作排在事件队列里”这样的理解其实也没有任何问题但如果深入的话会引出很多其他概念,比如event table和event queue, 我们来看运行过程:
- 首先判断JS是同步还是异步,同步就进入主线程运行,异步就进入event table.
- 异步任务在event table中注册事件,当满足触发条件后,(触发条件可能是延时也可能是ajax回调),被推入event queue
- 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程中
那事件循环机制到底是怎么样的?
不同类型的任务会进入对应的event queue, 比如setTime和setIntval会进入相同(宏任务)的event queue, 而Promise和process.nextTick会进入相同(微任务)的event queue.
Promise与事件循环
Promise在初始化时,传入的函数是同步执行的,然后注册then回调。注册完之后,继续往下执行同步代码,在这之前,then的回调不会执行。同步代码块执行完毕后,才会在事件循环中检测是否有可用的promise回调,如果有,那么执行,如果没有,继续下一个事件循环。
1. 宏任务,微任务都是队列, 一段代码执行时,会先执行宏任务中的同步代码。
2. 进行第一轮事件循环的时候会把全部的js脚本当成一个宏任务来运行。
3. 如果执行中遇到setTimeout之类的宏任务,那么就把这个setTimeout内部的函数推入[宏任务的队列]中,下一轮宏任务执行时调用。
4. 如果执行中遇到promise.then()之类的微任务,就会推入到[当前宏任务的微任务队列]中, 在本轮宏任务的同步代码都执行完成后,依次执行所有的微任务。
5. 第一轮事件循环中当执行完全部的同步脚步以及微任务队列中的事件,这一轮事件循环就结束了, 开始第二轮事件循环。
6. 第二轮事件循环同理先执行同步脚本,遇到其他宏任务代码块继续追加到[宏任务的队列]中,遇到微任务,就会推入到[当前宏任务的微任务队列]中,在本轮宏任务的同步代码执行都完成后, 依次执行当前所有的微任务。
7. 开始第三轮循环往复..
我们来看在任务队列中async/await的运行机制,先给出大概方向再通过案例来证明:
async定义的是一个promise函数和普通函数一样只要不调用就不会进入事件队列。
async内部如果没有主动return promise, 那么async会把函数的返回值用promise包装
await关键字必须出现在async函数中,await后面不是必须要跟一个异步操作,也可以是咦个普通表达式
遇到await关键字,await右边的语句会被立即执行然后await下面的代码进入等待状态,等待await得到结果。
await后面如果不是promise对象,await会阻塞后面的代码,先执行async外面的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为await表达式的结果。await后面如果是promise对象,await也会暂停async后面的代码,先执行async外面的同步代码,等着promise对象fulfilled,然后把resolve的参数作为await表达式的运算结果。