Node.js异步演进过程

Node.js是事件机制地处理请求中的io调用,这时就有了异步的概念出现,也就是说先去做其他事情,等io处理好了,再接受通知,并执行后续操作。下面我们来聊聊,Node.js里面怎么写异步的代码,异步流程控制概念。

首先异步的第一重境界是回调函数。Node常见的异步api都会接受一个函数参数,叫回调函数,也就是上面提到的接受通知后的后续操作都在这个回调函数中执行。对几个连续的异步操作的同步化就是异步流程控制。

回调函数示例

1
2
3
4
5
6
7
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
fs.stat('/tmp/world', (err, stats) => {
if (err) throw err;
console.log(`stats: ${JSON.stringify(stats)}`);
});
});

上面rename的第三个参数以及stat的第二个参数均为回调函数。

express框架早期风格就是以回调的方式处理异步。

这种方式不太好的地方是当同步化的时候,就会导致代码嵌套层级过深,形成回调地狱。

回调地狱举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
fs.rename('/tmp/world', '/tmp/hello', (err, stats) => {
if (err) throw err;
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
fs.rename('/tmp/world', '/tmp/hello', (err, stats) => {
if (err) throw err;
fs.rename('/tmp/hello', '/tmp/world', (err) => {
if (err) throw err;
fs.rename('/tmp/world', '/tmp/hello', (err, stats) => {
if (err) throw err;
console.log('end')
});
});
});
});
});
});
});

这样写的原因是保证每个执行流程是顺序的。然而造成了代码的可读性差。

这个时候Node.js出现了一个库叫co,这个库实现了一个叫generator的操作。让我们可以更好的控制异步的流程。这个库最后被应用于koa第一代中。

co库写法示例:

1
2
3
4
5
6
7
8
co(function* () {
var result = yield Promise.resolve(true);
return result;
}).then(function (value) {
console.log(value);
}, function (err) {
console.error(err.stack);
});

这种写法有两个地方值得注意,一是function关键字后面有一个*,同时使用了yield关键字,这个*代表了这是个generator方法,有了这个*就可以方法体内使用yield关键字。yield关键字代表等待后面的操作执行完了以后才返回值,此时会造成挂起,这也是generator最初设计的本意(并不是完全为了解决异步的问题而产生,只是刚好能解决这个问题)。

提完了generator,顺便提一下现在真正使用的比较多的async方法,这个方法和generator在形式上的不同就是把*变成了async并放在function关键字的前面,然后yield变成了await,对代码的构成和产生的效果类似。

async方法示例:

1
2
3
4
5
router.post('/delete_topic', async (req, res, next) => {
const { id } = req.body
const result = await forumManager.deleteTopic({ id })
res.json(result)
})

await后面跟的是一个Promise对象,也就是说forumManager.deleteTopic({ id })执行后会返回一个Promise对象。

什么是Promise对象呢,Promise有个A+规范,只要实现了这个规范的对象就是Promise对象。Promise也是用来做异步流程控制的。如果想知道Promise的应用可以看看相关的资料。

由于本人知识有限,可能有所疏漏之处,欢迎指正。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com

×

喜欢就点赞,疼爱就打赏