Node-JS专精10_01Promise2_7之后的规范

参考规范文档

代码实现

src/promise.ts

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
class Promise2{

succeed = null;
fail = null;
state = "pending";
callbacks = [];

resolve(result){
if(this.state !== "pending") return;
this.state = "fulfilled";
setTimeout(() => {
// 遍历 callbacks 调用所有的 handle[0]
this.callbacks.forEach(handle=>{
if(typeof handle[0] === "function"){
// x 是之前成功的返回值
const x = handle[0].call(undefined,result);
handle[2].resolveWith(x);
}
})
});
}
reject(reason){
if(this.state !== "pending") return;
this.state = "rejected";
setTimeout(() => {
this.callbacks.forEach(handle=>{
if(typeof handle[1] === "function"){
// x 是之前失败的返回值
const x = handle[1].call(undefined,reason);
handle[2].resolveWith(x);
}
})
});
}
constructor(fn){
if(typeof fn !== 'function'){
throw new Error("我只接受函数")
}

fn(this.resolve.bind(this), this.reject.bind(this));
}
then(succeed?,fail?){
// handle除了记录成功和失败 还要记录成功和失败的后续
const handle = [];
if(typeof succeed === 'function'){
handle[0] = succeed;
}
if(typeof fail === 'function'){
handle[1] = fail;
}

// 记录then之后的后续
handle[2] = new Promise2(()=>{});

this.callbacks.push(handle);
// 把函数推进 callBacks 里面

return handle[2];
}

resolveWith(x){
if( this === x){
this.reject(new TypeError())
} else if ( x instanceof Promise2){
x.then(
(result)=>{
this.resolve(result)
},
(reason)=>{
this.reject(reason)
}
)
} else if ( x instanceof Object){
let then;
try{
then = x.then;
}catch(e){
this.reject(e);
}
if(then instanceof Function){
try {
x.then(
y => {
this.resolveWith(y)
},
r =>{
this.reject(r);
}
);
} catch (error) {
this.reject(error)
}
} else {
this.resolve(x);
}
} else {
this.resolve(x);
}
}
}

export default Promise2

test/index.ts

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
import * as chai from "chai";
import * as sinon from "sinon";
import * as sinonChai from "sinon-chai";
chai.use(sinonChai);
const assert = chai.assert;
import Promise from "../src/promise";

describe("Promise",()=>{
it("是一个类",()=>{
assert.isFunction(Promise);
assert.isObject(Promise.prototype);
})
it("new Promise() 如果接受的不是一个函数就报错",()=>{
assert.throw(()=>{
// @ts-ignore
new Promise();
})
assert.throw(()=>{
// @ts-ignore
new Promise(1);
})
assert.throw(()=>{
// @ts-ignore
new Promise(false);
})
})

it("new Promise(fn) 会生成一个对象,对象有 then 方法",()=>{
const promise = new Promise(()=>{})
assert.isFunction(promise.then);
})

it("new Promise(fn) 中的 fn立即执行",()=>{
let fn = sinon.fake();
new Promise(fn);
assert(fn.called);
})
it("new Promise(fn) 中的 fn 执行的时候接受 resolve 和 reject 两个函数",(done)=>{
new Promise((resolve,reject)=>{
assert.isFunction(resolve);
assert.isFunction(reject);
done();
})
})

it("promise.then(success) 重的 success 会在 resolve 被调用的时候执行",(done)=>{
const success = sinon.fake();
const promise = new Promise((resolve,reject)=>{
// 该函数没有执行
assert.isFalse(success.called);
resolve();
setTimeout(() => {
// 该函数执行了
assert.isTrue(success.called);
done();
});
})
// @ts-ignore
promise.then(success);
})

it("promise.then(null,fail) 重的 fail 会在 reject 被调用的时候执行",(done)=>{
const fail = sinon.fake();
const promise = new Promise((resolve,reject)=>{
// 该函数没有执行
assert.isFalse(fail.called);
reject();
setTimeout(() => {
// 该函数执行了
assert.isTrue(fail.called);
done();
});
})
// @ts-ignore
promise.then(null,fail);
})

it("2.2.1",()=>{
const promise = new Promise((resolve,reject)=>{
resolve();
})
promise.then(false,null);
assert(1 === 1);
})

it("2.2.2",(done)=>{
const success = sinon.fake();
const promise = new Promise((resolve,reject)=>{
assert.isFalse(success.called);
resolve(233);
resolve(2333);
setTimeout(() => {
assert(promise.state === "fulfilled")
assert.isTrue(success.called);
assert.isTrue(success.calledOnce);
assert(success.calledWith(233));
done();
},0);
})
promise.then(success);
})

it("2.2.3",(done)=>{
const fail = sinon.fake();
const promise = new Promise((resolve,reject)=>{
assert.isFalse(fail.called);
reject(233);
reject(2333);
setTimeout(() => {
assert(promise.state === "rejected")
assert.isTrue(fail.called);
assert.isTrue(fail.calledOnce);
assert(fail.calledWith(233));
done();
},0);
})
promise.then(null,fail);
})

it("2.2.4 在我的代码执行完之前,不得调用 then 后面的两个函数 success",(done)=>{
const success = sinon.fake();
const promise = new Promise((resolve)=>{
resolve();
})
promise.then(success);
assert.isFalse(success.called);
setTimeout(() => {
assert.isTrue(success.called);
done();
}, 0);
})

it("2.2.4 在我的代码执行完之前,不得调用 then 后面的两个函数 fail",(done)=>{
const fail = sinon.fake();
const promise = new Promise((resolve,reject)=>{
reject();
})
promise.then(null,fail);
assert.isFalse(fail.called);
setTimeout(() => {
assert.isTrue(fail.called);
done();
}, 0);
})

it("2.2.5 不带入额外的this",(done)=>{
const fn = sinon.fake();
const promise = new Promise((resolve)=>{
resolve();
})
promise.then(function(){
"use strict";
assert(this === undefined);
done();
});
})

it("2.2.6 then可以在同一个promise里被多次调用(链式调用)",(done)=>{
const promise = new Promise((resolve)=>{
resolve();
})
const callbacks = [sinon.fake(),sinon.fake(),sinon.fake()]
promise.then(callbacks[0]);
promise.then(callbacks[1]);
promise.then(callbacks[2]);
setTimeout(() => {
assert(callbacks[0].called);
assert(callbacks[1].called);
assert(callbacks[2].called);
assert(callbacks[1].calledAfter(callbacks[0]));
assert(callbacks[2].calledAfter(callbacks[1]));

done();
});
})

it("2.2.6.2 then可以在同一个promise里被多次调用(链式调用) reject",(done)=>{
const promise = new Promise((resolve,reject)=>{
reject();
})
const callbacks = [sinon.fake(),sinon.fake(),sinon.fake()]
promise.then(null,callbacks[0]);
promise.then(null,callbacks[1]);
promise.then(null,callbacks[2]);
setTimeout(() => {
assert(callbacks[0].called);
assert(callbacks[1].called);
assert(callbacks[2].called);
assert(callbacks[1].calledAfter(callbacks[0]));
assert(callbacks[2].calledAfter(callbacks[1]));

done();
});
})

it("2.2.7 then必须返回一个 promise ",()=>{
const promise = new Promise((resolve)=>{
resolve();
})
const promise2 = promise.then(()=>{},()=>{});
assert(promise2 instanceof Promise);
});

it("2.2.7.1 如果onFulfilled或onRejected返回一个值x, 运行Promise Resolution Procedure [[Resolve]](promise2, x)",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
promise1.then(()=>"成功",()=>{}).then(result=>{
assert.equal(result,"成功");
done();
})
})

it("2.2.7.2 x 是一个 Promise实例",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
const fn = sinon.fake();
const promise2 = promise1.then(()=> new Promise(resolve=>resolve()),()=>{})
promise2.then(fn);

/*
注意 setTimeout 里的 延时时间
如果是0 测试不通过
如果是10 就通过
因为这里涉及 js 的 宏任务 微任务
宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务:Promise,process.nextTick(node实现的) / setImmediate(这个是IE实现的)
*/
setTimeout(() => {
assert(fn.called);
done();
},10);
})

})

宏任务/微任务

上述实现的一个测试代码

  • 由于我们的 Promise 使用的 setTimeout 实现的
  • 所以测试的时候那个 setTimeout 里的 延时时间设置为0会不通过
  • 因为微任务比宏任务的优先级更高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
it("2.2.7.2  x 是一个 Promise实例",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
const fn = sinon.fake();
const promise2 = promise1.then(()=> new Promise(resolve=>resolve()),()=>{})
promise2.then(fn);

/*
注意 setTimeout 里的 延时时间
如果是0 测试不通过
如果是10 就通过
因为这里涉及 js 的 宏任务 微任务
宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务:Promise,process.nextTick(node实现的) / setImmediate(这个是IE实现的)
*/
setTimeout(() => {
assert(fn.called);
done();
},10);
})

改用node环境来写代码

  • 使用process.nextTick 代替 setTimeout
  • yarn add --dev @types/node
  • 添加一个配置,告诉ts你写的是node应用,参考ts官网
  • tsconfig.json
    1
    2
    3
    4
    5
    {
    "compilerOptions": {
    "types" : ["node", "mocha"]
    }
    }

src/promise_node.ts

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
class Promise2{
state = "pending";
callbacks = [];

resolve(result){
if(this.state !== "pending") return;
this.state = "fulfilled";
process.nextTick(() => {
// 遍历 callbacks 调用所有的 handle[0]
this.callbacks.forEach(handle=>{
if(typeof handle[0] === "function"){
let x;
try {
// x 是之前成功的返回值
x = handle[0].call(undefined,result);
} catch (e) {
return handle[2].reject(e);
}
handle[2].resolveWith(x);
}
})
});
}
reject(reason){
if(this.state !== "pending") return;
this.state = "rejected";
process.nextTick(() => {
this.callbacks.forEach(handle=>{
if(typeof handle[1] === "function"){
let x;
try {
// x 是之前失败的返回值
x = handle[1].call(undefined,reason);
} catch (e) {
return handle[2].reject(e);
}
handle[2].resolveWith(x);
}
})
});
}
constructor(fn){
if(typeof fn !== 'function'){
throw new Error("我只接受函数")
}

fn(this.resolve.bind(this), this.reject.bind(this));
}
then(succeed?,fail?){
// handle除了记录成功和失败 还要记录成功和失败的后续
const handle = [];
if(typeof succeed === 'function'){
handle[0] = succeed;
}
if(typeof fail === 'function'){
handle[1] = fail;
}

// 记录then之后的后续
handle[2] = new Promise2(()=>{});

this.callbacks.push(handle);
// 把函数推进 callBacks 里面

return handle[2];
}

resolveWith(x){
if( this === x){
this.reject(new TypeError())
} else if ( x instanceof Promise2){
x.then(
(result)=>{
this.resolve(result)
},
(reason)=>{
this.reject(reason)
}
)
} else if ( x instanceof Object){
let then;
try{
then = x.then;
}catch(e){
this.reject(e);
}
if(then instanceof Function){
try {
x.then(
y => {
this.resolveWith(y)
},
r =>{
this.reject(r);
}
);
} catch (error) {
this.reject(error)
}
} else {
this.resolve(x);
}
} else {
this.resolve(x);
}
}
}

export default Promise2

test/index_node.ts

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
123
124
125
126
127
128
129
130
131
132
133
import * as chai from "chai";
import * as sinon from "sinon";
import * as sinonChai from "sinon-chai";
chai.use(sinonChai);
const assert = chai.assert;
import Promise from "../src/promise_node";

describe("Promise",()=>{
it("2.2.7.1 如果onFulfilled或onRejected返回一个值x, 运行Promise Resolution Procedure [[Resolve]](promise2, x)",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
promise1.then(()=>"成功",()=>{}).then(result=>{
assert.equal(result,"成功");
done();
})
})

it("2.2.7.1.2 success 的返回值是一个 Promise实例",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
const fn = sinon.fake();
const promise2 = promise1.then(()=> new Promise(resolve=>resolve()),()=>{})
promise2.then(fn);

/*
注意 setTimeout 里的 延时时间
如果是0 测试不通过
如果是10 就通过
因为这里涉及 js 的 宏任务 微任务
宏任务一般是:包括整体代码script,setTimeout,setInterval。
微任务:Promise,process.nextTick(node实现的) / setImmediate(这个是IE实现的)
*/
setTimeout(() => {
assert(fn.called);
done();
},0);
})

it("2.2.7.1.2 success 的返回值是一个 Promise实例 且失败了",(done)=>{
const promise1 = new Promise((resolve)=>{
resolve();
})
const fn = sinon.fake();
const promise2 = promise1.then(()=> new Promise((resolve,reject)=>reject()),()=>{})
promise2.then(null,fn);

setTimeout(() => {
assert(fn.called);
done();
},0);
})

it("2.2.7.1.2 fail 的返回值是一个 Promise实例",(done)=>{
const promise1 = new Promise((resolve,reject)=>{
reject();
});
const fn = sinon.fake();
const promise2 = promise1
.then(
null,
()=> new Promise(resolve=>resolve())
)
promise2.then(fn,null);

setTimeout(() => {
assert(fn.called);
done();
},0);
})

it("2.2.7.1.2 fail 的返回值是一个 Promise实例 且失败了",(done)=>{
const promise1 = new Promise((resolve,reject)=>{
reject();
});
const fn = sinon.fake();
const promise2 = promise1
.then(
null,
()=> new Promise((resolve,reject)=>reject())
)
promise2.then(null, fn);

setTimeout(() => {
assert(fn.called);
done();
},0);
})

it("2.2.7.1.2 如果 success 抛出一个异常e , promise2 必须被拒绝",(done)=>{
const promise1 = new Promise((resolve,reject)=>{
resolve();
});
const fn = sinon.fake();
const error = new Error();
const promise2 = promise1
.then(
()=> new Promise(()=>{
throw error;
})
)
promise2.then(null, fn);

setTimeout(() => {
assert(fn.called);
assert(fn.calledWith(error));
done();
},0);
})

it("2.2.7.1.2 如果 fail 抛出一个异常e , promise2 必须被拒绝",(done)=>{
const promise1 = new Promise((resolve,reject)=>{
reject();
});
const fn = sinon.fake();
const error = new Error();
const promise2 = promise1
.then(
null,
()=> new Promise(()=>{
throw error;
})
)
promise2.then(null, fn);

setTimeout(() => {
assert(fn.called);
assert(fn.calledWith(error));
done();
},0);
})
})

代码仓库