事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的。因此这里讲一下事件机制。php
在一个js文件中,若是要运行某一个函数,有2中手段,一个就是直接调用,好比foo(),第二就是利用事件来触发,这中函数也叫回调函数,好比传递给setTimeout函数和onready属性。css
setTimeout本质上也是一种异步事件,当延迟时间到的时候触发该事件,可是有的有的时候(其实也是大部分时候)都不会按照给定的延迟时间执行,先看下面的代码html
var start = new Date(); setTimeout(function() { console.log('settimeout1:',new Date()-start); }, 500); while (new Date() - start < 1000) { console.log('in while'); } document.getElementById('test').addEventListener('click', function(){ console.log('test:',new Date()-start); }, false) for(var i=0;i<10000;i++){ console.log('in for'); } setTimeout(function(){ console.log('settimeout2: ',new Date()-start); },1000); /* 10214 in while index.jsp (第 19 行) 10000 in for index.jsp (第 25 行) settimeout1: 2263 index.jsp (第 16 行) settimeout2: 3239 index.jsp (第 28 行) test: 10006 index.jsp (第 22 行) test: 28175 index.jsp (第 22 行) test: 28791 index.jsp (第 22 行) test: 28966 index.jsp (第 22 行) */
若是按照正常的理解,延迟函数应该在500毫秒以后打断while循环,而事实上并无,而且,我在while循环和for循环期间点击div时候并无当即输出test,给出的解释就是:node
a)事件队列。调用setTimeout函数的时候,会把传入它的回调函数加入到事件队列中去(事件已经初始化而且在内存了),而后继续执行后面的代码,直到再也没有代码能够运行(没有正常的运行流了,不包括事件函数等异步的内容),就会从事件队列里面pop出一个合适的事件来运行。jquery
b)js是单线程的,事件处理器在线程空闲以前是不会运行的。ajax
promise是一种解决ajax等异步编程回调函数嵌套太多致使代码晦涩难懂的解决方案,特别是在nodejs中,异步无处不在。不一样的框架对promise的实现,一下是jquery中的promise的API。编程
这里不讲promise的实现原理,关于原理在另外的篇幅中介绍。json
传统的ajax异步编程是这么写的(jquery1.5以前):promise
$.get('url', function(){ $.get('url1', function(){ $.get('url2', function(){ }, 'json'); }, 'json'); }, 'json');
这么写代码给开发和维护带来了极大的困难,好在jquery1.5之后引入了promise,就能够这么写了:浏览器
$.ajax( "example.php" ) .done(function() { alert("success"); }) .fail(function() { alert("error"); }) .always(function() { alert("complete"); });
如今看上去就明显简单多了。
var nanowrimoing = $.Deferred(); var wordGoal = 5000; nanowrimoing.progress(function(wordCount) { var percentComplete = Math.floor(wordCount / wordGoal * 100); $('#indicator').text(percentComplete + '% complete'); }); nanowrimoing.done(function(){ $('#indicator').text('Good job!'); });
脚本分为两大类:阻塞式和非阻塞式。这里的阻塞是指加载阻塞而不是运行阻塞。
<!DOCTYPE html> <html> <head> <script src="headScript"></script> <script defer src="deferredScript"></script> </head> <body> <script async defer src="chatWidget"></script> <script async defer src="asyncScript"></script> </body> </html>
上面这部分代码是比较标准的关于脚本在一个页面中的位置,1.其中传统的未加任何修饰的headScript是阻塞式的脚本,因为浏览器从上到下解释执行JavaScript,因此这部分脚本文件在一开始就会被执行,而且在执行完以前是DOM是不会渲染的,可是head标签里面的css会加载。2.有defer属性的脚本会在DOM渲染的同时进行加载,可是会在DOM渲染完毕以后才开始执行,不幸的是,不是全部的浏览器都支持defer属性,因此才会有了jquery(function)这个东西。3.同时带有async属性和defer属性时候,defer会覆盖async,可是单独有async的时候,脚本会在DOM渲染的时候加载而且运行。
若是不是一开始就在页面种引入js文件,而是经过用户交互来实现动态的加载js脚本,能够经过编程方式加入。
浏览器获取服务器脚本有2个方法,ajax获取而且经过eval函数执行,另一个就是在DOM中插入<script>标签,通常用第二种方法,由于浏览器帮助咱们生成HTTP请求以及eval会泄露做用域。
var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.src = '/js/feature.js'; head.appendChild(script);
script.onload = function() {
// 如今能够调用脚本里定义的函数了
}
不过,一些老版本的浏览器不支持onloa事件,因此可使用一些脚本加载库来实现这一功能,如require.js。