我们都知道,JS是一门单线程的脚本语言,单线程有个很大的问题就是阻塞;当我当前的动作没有完成时,后边的动作就要等着当前动作完成之后才能开始;那既然会有这么严重的,为什么JS还是选择了单线程?
JS是让用户通过用来操作DOM的,那么试想一下,若是多线程的话,我点一个按钮,是要增加一个标签,但是另一个线程还没走完,这个线程是要删除一个标签,那么浏览器就蒙圈了,我到底听谁的,一个让我加,一个让我删除?这样乱套了。所以JS最终成了一门单线程的语言,那么怎么去解决这个阻塞呢? 异步应运而生!
- // 代码块1// 下边我们看看这个例子,来体验一下异步的神奇;console.log(1);setTimeout(()=>{ console.log(2)},10)console.log(3);for(let i = 0; i < 999999999; i++){ // 这个for循环执行完成所需事件大于10秒}console.log(4)// 输入结果为: 1 3 4 2
复制代码
上边的代码是怎么执行的呢?
JS的异步有个大特点,须当同步代码执行完成后,才会再去执行异步,哪怕异步已经到了执行的时间了。在上述代码中,当for循环执行完成后,定时器早已到了执行的时间,但是他不能执行,因为同步代码还没有执行完成,异步需要在同步之后再去执行;
接下来,我们再去另一个例子:
- // 代码块2console.log(1)new Promise((res,rej)=>{ console.log(1111); res();}).then(()=>{ console.log(2222)})setTimeout(()=>{ console.log(2)},10)console.log(3)for(let i = 0; i < 999999999; i++){}console.log(4)//输出结果为: 1 1111 3 4 2222 2
复制代码 这段代码的执行顺序又是什么呢?
跟代码块1比较,这块就是多了一个Promise。那么这块是怎么执行的呢?- 在 new Promise时,我们给他传了一个回调函数,回调函数执行是一个同步函数,所以同步输出1111正常理解, res执行,应该输出2222 但是 then之后的函数执行是一个异步执行,所以需要等着同步执行完成,同步执行完成之后, 为什么要先输出2222 在输出2呢?我们可以这么理解, 当res执行是一个同步,我们想象一个队列,res执行时,我们可以理解成到时间了,但是也要等着同步执行完,所以res对应的函数要在排队,只不过排的是第一个,定时器再排过来就是第二个了;所以先输出2222,在输出1111.
复制代码 执行过程如下图
![]()
这里涉及到了事件循环(event loop)
事件循环其实就是入栈出栈的循环。上面例子中说到了setTimeout,Promise,有很多异步的函数。但是这些异步任务有分宏任务(macro-task)和微任务(micro-task):
宏任务(macro-task)包括: setTimeout, setInterval, setImmediate
微任务(micro-task)包括:Promises.then, Promise.catch,
每一次Event Loop触发时:
- 执行完主执行线程中的任务。
- 取出micro-task中任务执行直到清空。
- 取出macro-task中一个任务执行。
- 取出micro-task中任务执行直到清空。
- 重复3和4
本文选自网络,如侵,请联系删除
珠峰前端课程等你来学习
2019年6月24日 《零基础入门课程》
2019年7月8日 《全日制框架课程》
2019年7月8日 《周末班框架课程》
2019年6月24日 《前端就业课程》
|
|