点击同步按钮会调用updateSync的同步函数,逻辑非常简单,循环体内每次更新output结点的内容为i。如果在其他多线程模型下的语言,你可能会看到界面上以非常快的速度显示从0到999999后停止。但是在JavaScript中,你会感觉按钮按下去的时候卡了一下,然后看到一个最终结果999999,而没有中间过程,这就是因为在updateSync函数运行过程中UI更新被阻塞,只有当它结束退出后才会更新UI。反之,当点击异步的时候,会明显的看到Dom在逐步更新的过程。
从上面的例子中可以明显的看出,异步编程对于JavaScript来说是多么多么的重要。
异步编程有什么好处
从编程方式来讲当然是同步编程的方式更为简单,但是同步有其局限性一是假如是单线程那么一旦遇到阻塞调用,会造成整个线程阻塞,导致cpu无法得到有效利用,而浏览器的JavaScript执行和浏览器渲染是运行在单线程中,一旦遇到阻塞调用不仅意味JavaScript的执行被阻塞更意味整个浏览器渲染也被阻塞这就导致界面的卡死,若是多线程则不可避免的要考虑互斥和同步问题,而互斥和同步带来复杂度也很大,实际上浏览器下因为同时只能执行一段JavaScript代码这意味着不存在互斥问题,但是同步问题仍然不可避免,以往回调风格中异步的流程控制(其实就是同步问题)也比较复杂。浏览器端的编程方式也即是GUI编程,其本质就是事件驱动的(鼠标点击,Http请求结束等)异步编程更为自然。
突然有个疑问,既然如此为什么JavaScript没有使用多线程作业呢?就此就去Google了一下JavaScript多线程,在HTML5推出之后是提供了多线程只是比较局限。在使用多线程的时候无法使用window对象。若JavaScript使用多线程,在A线程中正在操作DOM,但是B线程中已经把该DOM已经删除了(只是简单的小栗子,可能还有很多问题,至于这些历史问题无从考究了)。会给编程作业带来很大的负担。就我而言我想这也就说明了为什么JavaScript没有使用异步编程的原因吧。
异步与回调
回调到底属于异步么?会想起刚刚开始学习JavaScript的时候常常吧这两个概念混合在一起。在搞清楚这个问题,首先要明白什么是回调函数。
百科:回调函数是一个函数,它作为参数传递给另一个函数,并在父函数完成后执行。回调的特殊之处在于,出现在“父类”之后的函数可以在回调执行之前执行。另一件需要知道的重要事情是如何正确地传递回调。这就是我经常忘记正确语法的地方。
通过上面的解释可以得出,回调函数本质上其实就是一种设计模式,例如我们熟悉的JQuery也只不过是遵循了这个设计原则而已。在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。
简单的举个小例子:
function test (n,fn){ console.log(n); fn && fn(n); } console.log(1); test(2); test(3,function(n){ console.log(n+1) }); console.log(5) // 结果 // 1 // 2 // 3 // 4 // 5
通过上面的代码输出的结果可以得出回调函数不一定属于异步,一般同步会阻塞后面的代码,通过输出结果也就得出了这个结论。回调函数,一般在同步情境下是最后执行的,而在异步情境下有可能不执行,因为事件没有被触发或者条件不满足。
回调函数应用场景
1.资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调,AJAX等等。
2.DOM事件及Node.js事件基于回调机制(Node.js回调可能会出现多层回调嵌套的问题)。
setTimeout的延迟时间为0,这个hack经常被用到,settimeout调用的函数其实就是一个callback的体现
3.链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现。
4.setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。callback的意义在于将timer执行的结果通知给代理函数进行及时处理。
JavaScript中的那些异步操作
JavaScript既然有很多的辅助线程,不可能所有的工作都是通过主线程去做,既然分配给辅助线程去做事情。
XMLHttpRequest