前言
因项目需要从LiveScript转为ES6, 所以最近看了阮一峰的ES6教程,主要感兴趣的是ES6对JS的异步编程新的解决方案,ES6增加了promise和Generator等解决方法。现在我们来大致理清一下到ES6为止的JS异步解决的思路以及他们各自的优缺点。
起因
我们都知道JS是单线程的,这也正是异步编程对于JS很重要的原因,因为它无法忍受耗时太长的操作。
正因如此有一系列的实现异步的方法
方法一 setTimeout
常用于:定时器,动画效果
用法:setTimeout(func|code, delay)
缺点:
setTimeout 的主要问题在于,它并非那么精确。譬如通过setTimeout()设定一个任务在10毫秒后执行,但是在9毫秒之后,有一个任务占用了5毫秒的CPU时间片,再次轮到定时器执行时,时间就已经过期4毫秒
—-《深入浅出Nodejs》
为什么呢? 我们可以了解一下setTimeout执行的事件循环图
Javascript执行引擎的主线程运行的时候,产生堆(heap)和栈(stack)。程序中代码依次进入栈中等待执行,当调用setTimeout()方法时,即图中右侧WebAPIs方法时,浏览器内核相应模块开始延时方法的处理,当延时方法到达触发条件时,方法被添加到用于回调的任务队列(注意是任务队列),只要执行引擎栈中的代码执行完毕,主线程就会去读取任务队列,依次执行那些满足触发条件的回调函数。
方法二 事件监听
任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
用法:f1.on(‘done’, f2);
优点:比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以”去耦合”,有利于实现模块化。
缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。
方法三 回调函数
注意:以下所有的例子中 a.md文件 存放的字符串为”b”, b.md文件存放的字符串为”this is b”;
什么是回调函数?
JavaScript语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
这里有一个误区
回调函数是实现JS异步的一种方法,并不是说回调函数就是异步的。
只是我们用的大多数回调函数都是用于异步
异步的定义:
在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。
因此callback 不一定用于异步,一般同步(阻塞)的场景下也经常用到回调,比如要求执行某些操作后执行回调函数。
简单的回调函数例子
var fs = require('fs');
fs.readFile("a.md", function(err, data) {
console.log(data.toString());
});