Matthew Note

A NodeJS Issue

描述


在《深入浅出NodeJS》一书中的第四章,给出了一个promise/deferred的一个简单实现,但是读来读去总觉得有些问题,所以拿来作者的代码做了个实验

实验代码


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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
var util = require("util");
var http = require("http");
var EventEmitter = require('events').EventEmitter;
var Deferred = function () {
this.state = 'unfulfilled';
this.promise = new Promise();
};
Deferred.prototype.resolve = function (obj) {
this.state = 'fulfilled';
this.promise.emit('success', obj);
console.log("emit success");
};
Deferred.prototype.reject = function (err) {
this.state = 'failed';
this.promise.emit('error', err);
console.log("emit error");
};
Deferred.prototype.progress = function (data) {
this.promise.emit('progress', data);
console.log("emit progress");
};
Deferred.prototype.all = function (promises) {
var count = promises.length;
var that = this;
promises.forEach(function (promise) {
promise.then(function () {
count--;
if (count === 0) {
that.resolve();
}
}, function () {
that.reject();
});
});
return this.promise;
};
var Promise = function () {
EventEmitter.call(this);
};
util.inherits(Promise, EventEmitter);
Promise.prototype.then = function (fulfilledHandler, errorHandler, progressHandler) {
if (typeof fulfilledHandler === 'function') {
// 利用once方法,保证成功回调只执行一次
this.once('success', fulfilledHandler);
console.log("register success");
}
if (typeof errorHandler === 'function') {
// 利用once方法,保证异常回调只执行一次
this.once('error', errorHandler);
console.log("register error");
}
if (typeof progressHandler === 'function') {
this.on('progress', progressHandler);
console.log("register progress");
}
return this;
};
var client = function(){
var options = {
hostname:'103.249.252.29',
port:80,
path:'/',
method: 'get'
};
var deferred = new Deferred();
var req = http.request(options, function(res){
res.setEncoding('utf-8');
var data = '';
console.log("callback called");
//必须consume这个data不然会一直保持下去,直到内存满了,所以可以晚点注册这个
//callback
res.on('data', function(chunk){
data += chunk;
console.log("res.on('data')");
deferred.progress(chunk);
});
res.on('end', function(){
console.log("res.on('end')");
deferred.resolve(data);
});
});
req.on('error', function(err){
deferred.reject(err);
})
req.end();
console.log("request sended");
return deferred.promise;
}
var c = client();
console.log("Going to call then1");
// c.then1(function(data){
// // console.log('请求完成', data);
// console.log('请求完成');
// }, function(err){
// console.log('访问错误', err);
// }, function(chunk){
// // console.log('正在读取', chunk);
// console.log('正在读取');
// });
setTimeout(function(){
c.then(function(data){
//console.log('请求完成', data);
console.log('请求完成');
}, function(err){
console.log('访问错误', err);
}, function(chunk){
//console.log('正在读取', chunk);
console.log('正在读取');
});
}, 10);

如果我们在最后setTimeout中设置10ms之后执行,那么then函数注册的三个callback可以正常处理response,结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
request sended
Going to call then1
register success
register error
register progress
callback called
res.on('data')
正在读取
emit progress
res.on('data')
正在读取
emit progress
res.on('data')
正在读取
emit progress
res.on('end')
请求完成
emit success

可以看出register callback发生在emit之前,这大概是网络IO速度比较慢造成的,那我们加大setTimeout延迟的时间到1000ms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
request sended
Going to call then1
callback called
res.on('data')
emit progress
res.on('data')
emit progress
res.on('data')
emit progress
res.on('end')
emit success
register success
register error
register progress

可以看到emit发生在register callback之前,所以then注册的callback并没有执行

结论


这实际上就是一个巧合,因为IO的低速导致了register callback之前,这种promise方式并不能保证then每次都能拿到结果