expressjs/finished Code Reading

##* finished

  • 功能: 在请求关闭,完成, 或者出错时执行回调
  • 用例: 清除传输流。例如为了防止文件描述符泄漏,在socket出错时, 你会想要将文件流摧毁

##从一个koa例子开始

1
2
3
4
5
6
7
var onFinished = require('finished');
function* () {
var stream = this.body = fs.createReadStream('thingie.json');
onFinished(this, function(err) {
stream.destroy();
})
}

再看finished的代码

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
37
38
module.exports = function finished (thingie, callback) {
/*
* 主要需要監測socket和res的事件
*/
var socket = thingie.socket || thingie;
var res = thingie.res || thingie;

/*
* res完成或者socket不可寫的時候, 直接執行callback, 返回thingie
*/
if ( res.finished || !socket.writable) {
defer(callback);
return thingie;
};

/*
* 第一次運行時listener 和 res.__onFinished 是不存在的
*/
var listener = res.__onFinished;

if (!listener || !listener.queue) {
listener = res.__onFinished = function onFinished(err) {
if (res.__onFinished === listener) res.__onFinished = null; // 這句表明每次給listener 賦完值, res.__onFinished = null;
var queue = listener.queue || [];
while (queue.length) queue.shift()(err)
}
listerner.queue = [];

first([
[socket, 'error', 'close'],
[res, 'finish'],
], listener); // 看[first](https://github.com/jonathanong/ee-first)模块解析: 主要功能在监听到指定的事件的第一件时,触发响应回调, 并清除移除所有的监听。
}

listener.queue.push(callback);

return thingie;
}

##想要具体看finish的应用场景,其实在单元测试里有很多具体的例子

  • 下面具体理解下其中的一个单元测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
describe('when the request finished', function () {
it('should execute the callback when called after finish', function(done){
var server = http.createServer(function(req, res){
onFinished(res, function(){
//第4步
onFinished(res, done); // 第5步
}) // 第2步
setTimeout(res.end.bind(res), 0); // 第3步
}) // 第 0 步
server.listen(function(){
var port = this.address().port;
http.get('http://127.0.0.1:' + port, function(){
res.resume();
res.on('close', server.close.bind(server));
}) //第1步
})
})
})
  • 测试步骤
    • 先创建服务器 第0步
    • 监听端口,做出请求 第一步
    • 服务器收到请求, 执行回调函数 onFinished(res, function(){}) 第2步
    • 第3步的话 结束请求 res.end.bind(res)
    • 第4步 监听到结束, 执行回调 onFinished(res, done)
    • 第5步 完成done
  • 测试结论:当服务器完成请求后,有onFinished()的话, 直接执行回调, 然后结束
...
2019-2024 zs1621