⚡ Promise 原理与实现完全指南
Promise 是 JavaScript 中处理异步操作的重要机制,理解其原理和实现对于深入掌握异步编程至关重要。
🎯 Promise 原理
js
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
const self = this; // 保存初始化状态
this.state = PENDING; // 初始化状态
this.value = null; // 用于保存 resolve 传入的值
this.reason = null; // 用于保存 rejected 传入的值
this.resolvedCallbacks = []; // 用于保存 resolve 的回调函数
this.rejectedCallbacks = []; // 用于保存 reject 的回调函数
// 状态转变为 resolved 方法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变,
if (self.state === PENDING) {
self.state = RESOLVED; // 修改状态
self.value = value; // 设置传入的值
// 执行回调函数
self.resolvedCallbacks.forEach(callback => callback(value));
}
}, 0);
}
// 状态转变为 rejected 方法
function reject(reason) {
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变
if (self.state === PENDING) {
self.state = REJECTED; // 修改状态
self.reason = reason; // 保存错误原因
// 执行回调函数
self.rejectedCallbacks.forEach(callback => callback(reason));
}
}, 0);
}
try {
fn(resolve, reject); // 将两个方法传入函数执行
} catch (e) {
reject(e); // 遇到错误时,捕获错误,执行 reject 函数
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
onResolved = typeof onResolved === "function" ? onResolved : value => value;
onRejected =
typeof onRejected === "function"
? onRejected
: error => {
throw error;
};
switch (this.state) {
case PENDING: // 如果是等待状态,则将函数加入对应列表中
this.onFulfilledCallbacks.push(onResolved);
this.onRejectedCallbacks.push(onRejected);
break;
case RESOLVED:
onResolved(this.value);
break;
case REJECTED:
onRejected(this.reason);
break;
}
};
// all方法实现
MyPromise.prototype.all = function (promises) {
if (!Array.isArray(promises)) throw new TypeError("参数类型错误");
return new Promise((resolve, reject) => {
let resolveCounter = 0;
const resolveResult = [];
for (let i = 0; i < promises.length; i++) {
promises[i].then(
value => {
resolveCounter++;
resolveResult[i] = value;
if (resolveCounter === promises.length) {
resolve(resolveResult);
}
},
error => {
reject(error);
}
);
}
});
};
// race方法实现
MyPromise.prototype.race = function (promises) {
if (!Array.isArray(promises)) throw new TypeError("参数类型错误");
return new Promise((resolve, reject) => {
for (const promise of promises) {
promise.then(resolve, reject);
}
});
};
MyPromise.prototype.finally = function (callback) {
// this:当前promise实例,this.constructor:Promise构造函数
const p = this.constructor;
return this.then(
value => p.resolve(callback()).then(() => value),
reason =>
p.reject(callback()).then(() => {
throw reason;
})
);
};
// promise封装AJAX
// promise 封装实现:
function getJSON(url) {
// 创建一个 promise 对象
return new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function () {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function () {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
}
// promisify函数,使函数promise化 readFile函数举例,node已经包含该方法在util模块中,util.promisify(func)
function promisify(func) {
return function (...arg) {
return new Promise((resolve, reject) => {
func(...arg, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
};
}
// fs.readFile(path,'utf-8',(err,data)=>{console.log(data);}) 如果err存在,则读取文件失败
const readFile = promisify(fs.readFile);
readFile(path, "utf-8").then(data => console.log(data));
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
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
promise 限制并发
js
class LimitPromise {
constructor(max) {
// 异步任务“并发”上限
this._max = max;
// 当前正在执行的任务数量
this._count = 0;
// 等待执行的任务队列
this._taskQueue = [];
}
/** 调用器:把真正的执行函数和参数传入,异步任务被调度时,创建返回一个新的Promise:创建一个任务,判断任务是执行还是入队。
* @param caller 异步任务函数,它必须是async函数或者返回Promise的函数
* @param args 异步任务函数的参数列表
* @returns {Promise<unknown>} 返回一个新的Promise
*/
call(caller, ...args) {
return new Promise((resolve, reject) => {
const task = this._createTask(caller, args, resolve, reject);
if (this._count >= this._max) {
// 超过最大并发限制则入队
this._taskQueue.push(task);
} else {
task();
}
});
}
/** 创建一个任务(包装函数):包装真正执行的函数。
* @param caller 实际执行的函数
* @param args 执行函数的参数
* @param resolve
* @param reject
* @returns {Function} 返回一个任务函数
* @private
*/
_createTask(caller, args, resolve, reject) {
return () => {
// 调用包装函数时,令执行数加一,执行实际的异步任务
this._count++;
caller(...args)
.then(resolve)
.catch(reject)
.finally(() => {
// 任务执行后,取出并执行下一个栈顶任务
this._count--;
if (this._taskQueue.length) {
let task = this._taskQueue.shift();
task();
}
});
};
}
}
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
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