摘要:这种问题在设置倒计时的经常遇到,倒计时开始的时候设置的时间是从服务器拿到的系统时间很准确,但是如果后面不定期像服务期请求系统时间进行校准的话,你可能会发现倒计时的偏差越来越来大,这就是主线程执行的时间比设定的延迟时间长导致的。
关于js执行机制,老早之前就一直想写篇文章做个总结,因为和js执行顺序的面试题碰到的特别多,每次碰到总是会去网上查,没有系统地总结,搞得每次碰到都是似懂非懂的感觉,这篇文章就系统的总结一下js执行机制。
任务队列大家都知道js最大的特点就是单线程执行,这就是为什么js简单易学的一个重要原因,不需要考虑复杂的同步问题,但是单线程也会有一个问题,所有的任务在执行的过程中都必须等待前一个任务执行完成才能执行,这样就会带来一个效率的问题,为了解决这个问题,js将任务分为两种:同步任务和异步任务,同步任务就是之前说后一个任务必须等待前一个任务执行完成才能执行,是在主线程上执行的,而异步任务不会直接进入主线程执行,而是进入任务队列,只有在任务队列通知异步任务可以执行时,才会被推入主线程执行。让我们来看一个更加直观的流程图:
setTimeout和setInterval说到异步任务,最常见就是setTimeout和setInterval两兄弟了,setTimeout是延迟一定时间后执行,但是只执行一次,setInterval是每隔一定的时间执行一次,会执行多次,但是有时候我们会发现设置一定的延迟时间后,回调函数的执行时间会比我们设置的时间要晚,这是为什么呢?上面我们说过,在任务执行的时候setTimeout这类异步任务的回调会被放到异步队列中等待执行,当延迟时间结束时,如果主线程的任务已经执行完了,也就是处在空闲状态时,就会将任务队列的回调推到主线程执行,但是当主线程的任务还没有执行完成时,就只能继续等待,来看一个例子:
let before = new Date() setTimeout(() => { console.log(new Date() - before) }, 1000) for (let i = 0; i < 300000; i++) { console.log("time delay") }
从上面的例子就可以看到:当我们执行完setTimeout之后,立刻执行20万次的循环,从执行结果可以看到,setTimeout回调函数中的时间远高于设置1000ms,这就是因为时间到了,但是主线程的任务还没有执行完成导致。这种问题在setInterval设置倒计时的经常遇到,倒计时开始的时候设置的时间是从服务器拿到的系统时间很准确,但是如果后面不定期像服务期请求系统时间进行校准的话,你可能会发现倒计时的偏差越来越来大,这就是主线程执行的时间比设定的延迟时间长导致的。
macrotask和microtask在js中,异步任务除了有setTimeout这类的异步任务,还有一类就是es6中很常用promise...then这类的异步任务,因此除了同步任务和异步任务,任务还可以更加细分为macrotask(宏任务)和microtask(微任务)
macrotask: 包括setTimeout、setInterval和执行栈
microtask: 包括Promise、process.nextTick
要想理解这两个概念,直接从一道简单的面试题入手,来看一个例子:
setTimeout(function() { console.log(1) }, 0); new Promise(function(resolve, reject) { console.log(2); resolve() }).then(function() { console.log(3) }); process.nextTick(function () { console.log(4) }) console.log(5)
思考一下上面例子的输出结果,我们来仔细分析一下执行过程:
第一轮:主线程开始执行,遇到setTimeout,将setTimeout的回调函数丢到宏任务队列中,在往下执行new Promise立即执行,输出2,then的回调函数丢到微任务队列中,再继续执行,遇到process.nextTick,同样将回调函数扔到为任务队列,再继续执行,输出5,当所有宏任务执行完成后看有没有可以执行的微任务,发现有then函数和nextTick两个微任务,先执行哪个呢?process.nextTick指定的异步任务总是发生在所有异步任务之前,因此先执行process.nextTick输出4然后执行then函数输出3,第一轮执行结束。
第二轮从宏任务队列开始,发现setTimeout回调,输出1执行完毕,因此结果是25431
最后用一张图来总结一下:
总结这篇文章简单介绍了js执行机制,希望看了之后,可以对大家认识js的执行机制会有所帮助。
如果有错误或不严谨的地方,欢迎批评指正,如果喜欢,欢迎点赞收藏
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/100076.html
摘要:事件循环背景是一门单线程非阻塞的脚本语言,单线程意味着,代码在执行的任何时候,都只有一个主线程来处理所有的任务。在意识到该问题之际,新特性中的可以让成为一门多线程语言,但实际开发中使用存在着诸多限制。这个地方被称为执行栈。 事件循环(Event Loop) 背景 JavaScript是一门单线程非阻塞的脚本语言,单线程意味着,JavaScript代码在执行的任何时候,都只有一个主线程来...
摘要:以多线程的形式,允许单个任务分成不同的部分进行运行。提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。主线程会不断的重复上诉过程。 众所周知,js是单线程的,说到线程,我们首先来仔细辨析一下线程和进程的知识。 一、进程与线程 阮一峰老师的一篇文章写的很好 cpu会给当前进程分配资源,进程是资源分配的最小单位,进程的资源会分配给线程使用,线程是C...
摘要:如果没有其他异步任务要处理比如到期的定时器,会一直停留在这个阶段,等待请求返回结果。执行的执行事件关闭请求的,例如事件循环的每一次循环都需要依次经过上述的阶段。因此,才会早于执行。 showImg(https://segmentfault.com/img/bVbnY76); 概念 同步任务(Synchronous) 在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务 ...
阅读 1871·2021-11-22 14:44
阅读 1637·2021-11-02 14:46
阅读 3577·2021-10-13 09:40
阅读 2574·2021-09-07 09:58
阅读 1458·2021-09-03 10:28
阅读 1628·2019-08-29 15:30
阅读 945·2019-08-29 15:28
阅读 1438·2019-08-26 12:20