Javascript消息队列
-
这是我的第一篇博文,先显得正式一点...
了解Javascript的同学都知道,JS提供的一些函数是同步的,有些是异步的。但是我们在实际实现某个功能的时候不得不用到有一些的异步函数当作同步函数来调用,但是这样的风险很大,甚至直接导致运行出错。现在我小小地分析一下JS的消息机制。
首先,Javascript是单线程的,也就是说同一个时间只能做一件事,原因是为了提高浏览器的运行效率。但也因此使得某些异步调用并不是同时进行的。
第二,Javascript存在任务队列,也就是消息队列。因为单线程的关系,不可能实现并行。设计者提出我们的Javascript在程序运行前创建两个队列,一个是异步队列,另一个是回调队列。在程序运行到同步函数代码时,必须先执行完这个函数才能执行下一个语句。而遇到异步函数,则把它放入异步队列,直接执行下一行代码了。回调也是如此。那么也就是说,异步函数并不会直接在当前行执行,只有在同步运行完了(函数结束或者某个循环体),才会调用异步队列里的函数。当异步队列为空,才开始调用回调队列的回调函数。
下图就是主线程和任务队列的示意图。
了解到了异步队列的运行机制,那么我们就可以分析出这样一个事实:“同步=>异步=>回调”。根据这个特性,很多奇怪的问题就可以分析解决了。例如以下讨论的经典异步定时器setTimeout。
setTimeout()接受两个参数,第一个是回调函数,第二个是推迟执行的毫秒数。例如以下代码:console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);上面代码的执行结果是1,3,2。为什么不是1,2,3呢?因为setTimeout()是异步的,JS把它放入了“异步队列”里,先执行了console.log(3),然后执行定时器,等待了一秒钟后输出2。
极端一点的:
setTimeout(function(){console.log(1);}, 0);
console.log(2);上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行"异步队列"中的回调函数。
又例如:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log('i: ',i);
}, 1000); }
console.log(i);输出 5 i: 5 i: 5 i: 5 i: 5 i: 5。因为程序运行时把setTimeout放入了异步队列,当for循环执行完了之后才开始调用异步队列,此时队列里已经有循环执行时排队的5个setTimeout函数,再依次出队执行,这个时候i都是5了。所以先输出“5”,再输出5个“i: 5”。
异步队列的运行机制,就是:“同步=>异步=>回调”。只要掌握了这个,这种系列的题目应该不难了。