随着对JS的深入,异步编程是每个JS程序员都跳不过的话题。不仅在前端开发有大量的异步事件处理,NODE更是有出了名的callback hell。为了更好地掌握JS异步编程,让我们从基础开始看起吧。
先看一个简单的例子:
for (var i = 1; i <= 3; i++) {
setTimeout(function() { console.log(i); }, 0);
}
若是从C、JAVA转来的程序员,第一直觉是setTimeout延时为0,因此程序应该打印1,2,3。但是程序执行的结果却是4,4,4。
再看下面一个列子:
var start = new Date;
setTimeout(function(){
var end = new Date;
console.log('Time elapsed:', end - start, 'ms');
}, 500);
while (new Date - start < 1000) {};
SetTimeout设置了500ms的延迟,而随后的while则空循环等待1000ms。直觉的反应是console会打印500,但执行的结果却是出乎意料的约等于1000。
这里充分说明了一个问题:setTimeout没有调用另一个线程,JS程序的执行是基于单线程模型的。于是当调用setTimeout的时候,这个延时事件便会排入事件队列。而线程继续执行后续的代码,直到代码全部执行完毕之后才来处理事件队列中的事件。
处理用户输入时也是类似的情况,当用户点击一个附加了事件处理函数的对象时,这个点击事件就排入事件队列。但该事件并不会被立即处理,而是要等到当前代码全部运行结束后才会被处理。
小结:JS是单线程执行的,JS代码永远不会被中断,所有的事件都会被排入事件队列,直到代码执行完毕后才会处理这些事件。
P.S: webkit的console.log也是异步的,在浏览器中执行:
var obj = {};
console.log(obj);
obj.foo = 'bar';
console并没有立即打印空对象,而是等到代码返回到事件队列的时候才打印对象,此时打印是对象{foo:bar}。
与此相反,NODE中console是同步的,将会立即打印{}。