logo头像
Snippet 博客主题

前端面试必考题Promise的源码解析

本文于997天之前发表,文中内容可能已经过时。

考察知识点:

  • js迭代器
  • js构造函数、原型、原型链
  • js执行栈、事件循环机制

伪源码实现:

Promise构造函数

  • 创建一个Promise实例,通过new一个Promise(接受一个函数作为参数),该函数接受resolve, reject两个方法作为参数。
  • 每个Promise只有三种状态:pendingfulfilledrejected,状态只能从 pending 转移到 fulfilled 或者 rejected,一旦状态变成fulfilled 或者 rejected,就不能再更改其状态。
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
function Promise3(executor) {
const self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];

function resolve(value) {
if (self.status === 'pending') {
self.status = 'resolved';
self.value = value;
self.onResolvedCallbacks.forEach(fn => fn());
}
}

function reject(reason) {
if (self.status === 'pending') {
self.status = 'rejected';
self.reason = reason;
self.onRejectedCallbacks.forEach(fn => fn());
}
}

try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}

定义在Promise原型上方法then,可被实例调用

  • 通过判断上一个Promise的状态执行不同的代码
  • 正常情况下第一个Promise执行器中是异步代码,会先执行then()函数,then函数两个参数onFulfilledonRejected会被暂存到onResolvedCallbacksonRejectedCallbacks中,在第一步的异步执行器executor(resolve, reject)中完成执行
  • 为了实现Promise的链式调用,在then方法中return promise,所以在new Promise的执行器中手动加了setTimeout实现异步执行
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
Promise3.prototype.then = function (onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
const self = this;
let promise2;
if (self.status === 'resolved') {
promise2 = new Promise3(function(resolve, reject) {
setTimeout(function () {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status === 'rejected') {
promise2 = new Promise3(function(resolve, reject) {
setTimeout(function () {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
});
}
if (self.status === 'pending') {
promise2 = new Promise3(function(resolve, reject) {
self.onResolvedCallbacks.push(function () {
setTimeout(function() {
try {
let x = onFulfilled(self.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
onRejected(e);
}
});
});
self.onRejectedCallbacks.push(function () {
setTimeout(function() {
try {
let x = onRejected(self.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
onRejected(e);
}
});
});
});
}
return promise2;
}

定义resolvePromise方法

  • 上一个Promise返回值有3中情况(也是该函数取名的缘由)
    1. 普通值
    2. promise对象
    3. thenable对象/函数
  • 通过自身迭代和在then方法中的引用,直至返回resolve普通值或者报错
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
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle')); // 循环引用报错
}

let called;
if (x && (typeof x === 'object' || typeof x === 'function')) {
try {
let then = x.then;
if (typeof then === 'function') {
then.call(x, function(y) {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
}, function(err) {
if (called) return;
called = true;
reject(err);
});
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}

Promise的静态方法及其它方法:

Promise.resolve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.resolve = function (param) {
if (param instanceof Promise) {
return param;
}
return new Promise((resolve, reject) => {
if (param && param.then && typeof param.then === 'function') {
setTimeout(() => {
param.then(resolve, reject);
});
} else {
resolve(param);
}
});
}

Promise.reject

1
2
3
4
5
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}

Promise.prototype.catch

1
2
3
Promise.prototype.catch = function (onRejected) {
return this.then(null, onRejected);
}

Promise.prototype.finally

1
2
3
4
5
6
7
8
9
10
11
Promise.prototype.catch = function (callback) {
return this.then((value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
}, (err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
});
}

Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let index = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
function processValue(i, data) {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
}
promises.forEach((promise, i) => {
Promise.resolve(promise).then((data) => {
processValue(i, data);
}, (err) => {
reject(err);
return;
});
});
}
});
}

Promise.race

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return;
} else {
promises.forEach((promise, i) => {
Promise.resolve(promise).then((data) => {
resolve(data);
return;
}, (err) => {
reject(err);
return;
});
});
}
});
}

promise测试库

1
2
npm install promises-aplus-tests -D
npx promises-aplus-tests promise.js

参考文章:

微信打赏

赞赏是不耍流氓的鼓励