异步编程(一)

笔记部分内容摘自 《深入浅出 Node.JS》 —— 朴灵

异步编程难点

  • 异常处理
  • 函数嵌套
  • 阻塞代码
  • 多线程
  • 异步转同步

异常处理

传统 try/catch 方法,只能捕获当次事件循环内的异常, 对callback执行时,无法再进行捕获。

1
2
3
4
5
6
7
try {
//...
async(callback);
//...
} catch (e) {
//TODO
}

callback会在下一次事件循环时执行,callback中的异常不能正常捕获,而直接抛出,导致程序crash

Sample:

程序会因为未捕获error而崩溃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try {
asyncFunc(func);
} catch(e) {
console.log(e);
}

function asyncFunc (callback) {
console.log('asyncFunc');
setTimeout(function () {
callback();
}, 1000);
}

function func() {
console.log('func');
var error = new Error('Error Not Catch');
throw error;
}

一般将出现的异常或错误,作为回调函数的第一个实参返回,如果为空值,则表明异步调用没有异常抛出。

1
2
3
4
5
6
async(function(err, results) {
if (err) {
//TODO
}
//TODO
})

自行编写异步方法时需要遵循的原则:

  1. 必须执行调用者传入的回调函数
  2. 正确传递回异常供调试者判断

try/catch易对用户传递的回调函数进行异常捕获

Sample:

1
2
3
4
5
6
7
8
try {
req.body = JSON.parse(string);
callback();
//...
} catch(err) {
//...
callback(err);
}

本来是对JSON.parse()进行异常捕获,但是如果当callback中出现异常抛出,将会导致回调执行两次。

正确写法:

1
2
3
4
5
6
7
8
9
try {
req.body = JSON.parse(string);
//...
} catch(err) {
//...
return callback(err);
}

callback();

函数嵌套

恶魔金字塔问题、回调地狱

阻塞代码

延时操作: setInterval()/setTimeout()
但是并不延迟后续代码的执行。

1
2
3
4
5
6
//...
var now = new Date();
while(new Date() - now < 1000) {

}
// 被阻塞的代码

这种写法完全阻塞代码,破坏了事件循环的调度。再加上由于Node.js的单线程原因,CPU资源会完全被这段代码占用,导致任何其余请求都得不到相应。

多线程编程

Node.js 单线程应用, 不能充分利用系统资源。
可通过多部署,子进程、集群等解决。在后面的学习中补充。

异步转同步

Node提供API大部分是异步,同步API较少,在同步需求下,需要借助库或编译等手段来实现。