异步与同步

异步与同步

从调用方式上,可以把他们分为3类: 同步调用,回调和异步调用。同步调用是一种阻塞式的调用,调用要等待对方执行完毕才返回,他是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件机制,不过他的调用方向刚好相反, 接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(就是调用客户方的接口)。

  • 实例
    1
    2
    3
    var fs = require('fs');
    var data = fs.readFileSync('new.txt');
    console.log(data.toString());

readFileSync是同步方法,会阻塞直接得到结果后,才继续执行之后的语句。

1
2
3
4
5
var fs = require('fs');
fs.readFile('new.txt', function callback(err, data){
console.log(data.toString());
});
console.log('first me run');

会发现console.log(‘first me run’)先执行。因为readfile是异步函数,不会阻塞之后的语句。callback是回调函数,等到底层读完数据后, 会调用该函数。
node.js中回调的函数的规范是:

1
2
3
function callback(err, args0, args1...) {

}

下面模拟一个异步函数,和回调函数,加深理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// function asyncFun(a, b, cb) {
// setTimeout(function () {
// cb(a + b);
// }, 200);
// }
// //异步的
// asyncFun(1, 2, function (result) {
// if(result > 2){
// asyncFun(result, 2, function (result) {
// if(result > 4){
// asyncFun(result, 2, function (result) {
// console.log('ok');
// });
// }
// });
// }
// });
// console.log(2);
function asyncFun(a, b) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(a + b);
}, 200);
});
}
asyncFun(1, 2)
.then(function (result) {
if(result > 2){
return asyncFun(result, 2);
}
})
.then(function (result) {
if(result > 4){
console.log('ok');
}
});

可以看到,Promise 的写法只是回调函数的改进,使用then方法以后,异步任务的两段执行看得更清楚了,除此以外,并无新意。

Promise 的最大问题是代码冗余,原来的任务被Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。