异步与同步
从调用方式上,可以把他们分为3类: 同步调用,回调和异步调用。同步调用是一种阻塞式的调用,调用要等待对方执行完毕才返回,他是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件机制,不过他的调用方向刚好相反, 接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(就是调用客户方的接口)。
- 实例
1
2
3var fs = require('fs');
var data = fs.readFileSync('new.txt');
console.log(data.toString());
readFileSync是同步方法,会阻塞直接得到结果后,才继续执行之后的语句。
1 | var fs = require('fs'); |
会发现console.log(‘first me run’)先执行。因为readfile是异步函数,不会阻塞之后的语句。callback是回调函数,等到底层读完数据后, 会调用该函数。
node.js中回调的函数的规范是:1
2
3function callback(err, args0, args1...) {
}
下面模拟一个异步函数,和回调函数,加深理解。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function sumAsyn(a, b, callback) {
setTimeout(
function () {
if(typeof a === 'number' && typeof b === 'number'){
callback(null, a + b);
}else{
callback(new Error('must be number'));
}
}
, 200)
}
sumAsyn(2, 3, function callback(err, rs) {
console.log(rs);
});
console.log('first run!!');
我们定义一个sumAsyn异步函数,运行后先执行console.log(‘first run!!’)。
在node开始所有i/o应该用异步。
异步
所谓”异步”,简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。
回调函数
JavaScript语言对异步编程的实现,就是回调函数。所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。它的英语名字callback,直译过来就是”重新调用”。
一个有趣的问题是,为什么Node.js约定,回调函数的第一个参数,必须是错误对象err(如果没有错误,该参数就是null)?原因是执行分成两段,在这两段之间抛出的错误,程序无法捕捉,只能当作参数,传入第二段。
Promise
1 | // function asyncFun(a, b, cb) { |
可以看到,Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。
Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。