https://juejin.cn/post/6994594642280857630

初始架构

  1. promise实例被构造的时候,构造方法参数(resolve, reject)⇒{} 是立即执行的,所以这个函数参数要在constructor构造器中调用。
  2. 共有三种状态,pending,fulfilled,rejected,需要初始化状态为 pending,结果值为null
  3. 避免之后调用resolve/reject时,this状态改变,初始化时需要绑定this
  4. 一旦pending状态变更后,状态不能再改变,所以执行resolve/reject时需要加上条件判断
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
class MyPromise {
// 构造方法
constructor(executor) {
// 初始化值
this.initValue();
// 初始化this指向
this.initBind();
// 执行传进来的函数
executor(this.resolve, this.reject);
}

initBind() {
// 初始化this
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}

initValue() {
// 初始化值
this.PromiseResult = null; // 终值
this.PromiseState = "pending"; // 状态
}

resolve(value) {
// state是不可变的
if (this.PromiseState !== "pending") return;
// 如果执行resolve,状态变为fulfilled
this.PromiseState = "fulfilled";
// 终值为传进来的值
this.PromiseResult = value;
}

reject(reason) {
// state是不可变的
if (this.PromiseState !== "pending") return;
// 如果执行reject,状态变为rejected
this.PromiseState = "rejected";
// 终值为传进来的reason
this.PromiseResult = reason;
}
}

另外,如果promise中有throw的话,就相当于执行了reject。这就要使用try catch

1
2
3
4
5
6
7
try {
// 执行传进来的函数
executor(this.resolve, this.reject);
} catch (e) {
// 捕捉到错误直接执行reject
this.reject(e);
}

Promise 的 Then

  1. then接收两个回调,一个是成功回调,一个是失败回调
  2. Promise状态为fulfilled执行成功回调,为rejected执行失败回调
  3. resolvereject在定时器里,则定时器结束后再执行then
  4. then支持链式调用,下一次then执行受上一次then返回值的影响

定时器问题

在下面这个例子中,一秒之后状态才更新

1
2
3
4
5
6
7
8
9
// 1秒后输出 ”成功“
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("失败");
}, 1000);
}).then(
(res) => console.log(res),
(err) => console.log(err)
);

可以先把then里的两个回调保存起来,然后等到 1 秒过后,执行resolve或者reject,再去判断状态,并判断要去执行刚刚保存的两个回调中的哪一个回调。

/images/promise/1.png

只要状态是pending,那就证明定时器还没跑完,因为如果定时器跑完的话,那状态肯定就不是pending,而是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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
initValue() {
// 初始化值
this.PromiseResult = null // 终值
this.PromiseState = 'pending' // 状态
+ this.onFulfilledCallbacks = [] // 保存成功回调
+ this.onRejectedCallbacks = [] // 保存失败回调
}

resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
// 执行保存的成功回调
+ while (this.onFulfilledCallbacks.length) {
+ this.onFulfilledCallbacks.shift()(this.PromiseResult)
+ }
}

reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
// 执行保存的失败回调
+ while (this.onRejectedCallbacks.length) {
+ this.onRejectedCallbacks.shift()(this.PromiseResult)
+ }
}

then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected

// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
onFulfilled(this.PromiseResult)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二哥回调
onRejected(this.PromiseResult)
**+ } else if (this.PromiseState === 'pending') {
+ // 如果状态为待定状态,暂时保存两个回调
+ this.onFulfilledCallbacks.push(onFulfilled.bind(this))
+ this.onRejectedCallbacks.push(onRejected.bind(this))**
+ }

}

链式调用

then支持链式调用,下一次then执行受上一次then返回值的影响。

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
// 链式调用 输出 200
const p3 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
(res) => 2 * res,
(err) => console.log(err)
)
.then(
(res) => console.log(res),
(err) => console.log(err)
);

// 链式调用 输出300
const p4 = new Promise((resolve, reject) => {
resolve(100);
})
.then(
(res) => new Promise((resolve, reject) => resolve(3 * res)),
(err) => console.log(err)
)
.then(
(res) => console.log(res),
(err) => console.log(err)
);
  1. then方法本身会返回一个新的Promise对象
  2. 如果返回值是promise对象,返回值为成功,新promise就是成功
  3. 如果返回值是promise对象,返回值为失败,新promise就是失败
  4. 如果返回值非promise对象,新promise对象就是成功,值为此返回值

因此,then执行后返回一个Promise对象,就能保证then完还能继续执行then

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
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected

// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }

var thenPromise = new MyPromise((resolve, reject) => {

const resolvePromise = cb => {
try {
const x = cb(this.PromiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
}
}

if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
resolvePromise(onFulfilled)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
resolvePromise(onRejected)
} else if (this.PromiseState === 'pending') {
// 如果状态为待定状态,暂时保存两个回调
// 如果状态为待定状态,暂时保存两个回调
this.onFulfilledCallbacks.push(resolvePromise.bind(this, onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this, onRejected))
}
})

// 返回这个包装的Promise
return thenPromise

}

微任务

then方法是微任务,使用定时器,让resolvePromise函数异步执行,模拟微任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const resolvePromise = (cb) => {
setTimeout(() => {
try {
const x = cb(this.PromiseResult);
if (x === thenPromise) {
// 不能返回自身哦
throw new Error("不能返回自身。。。");
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject);
} else {
// 非Promise就直接成功
resolve(x);
}
} catch (err) {
// 处理报错
reject(err);
}
});
};

其他方法

all

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 如果所有Promise都成功,则返回成功结果数组
  • 如果有一个Promise失败,则返回这个失败结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve, reject) => {
const addData = (index, value) => {
result[index] = value
count++
if (count === promises.length) resolve(result)
}
promises.forEach((promise, index) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData(index, res)
}, err => reject(err))
} else {
addData(index, promise)
}
})
})
}

race

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 哪个Promise最快得到结果,就返回那个结果,无论成功失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
if (promise instanceof MyPromise) {
promise.then(res => {
resolve(res)
}, err => {
reject(err)
})
} else {
resolve(promise)
}
})
})
}

allSettled

  • 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
  • 把每一个Promise的结果,集合成数组,返回
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
static allSettled(promises) {
return new Promise((resolve, reject) => {
const res = []
let count = 0
const addData = (status, value, i) => {
res[i] = {
status,
value
}
count++
if (count === promises.length) {
resolve(res)
}
}
promises.forEach((promise, i) => {
if (promise instanceof MyPromise) {
promise.then(res => {
addData('fulfilled', res, i)
}, err => {
addData('rejected', err, i)
})
} else {
addData('fulfilled', promise, i)
}
})
})
}

any

  • 接收一个 Promise 数组,数组中如有非 Promise 项,则此项当做成功
  • 如果有一个 Promise 成功,则返回这个成功结果
  • 如果所有 Promise 都失败,则报错
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
	static any(promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((promise) => {
promise.then(val => {
resolve(val)
}, err => {
count++
if (count === promises.length) {
reject(new AggregateError('All promises were rejected'))
}
})
})
})
}
}

retry

  • 如果可以用 async await 的话,那么可以用 while 循环,通过同步的语法实现
  • 如果不能用 async await 的话,那么可以用 Promise 递归调用的方法实现

如果要求两次请求间隔一段时间,用 promise 会更方便

在外层使用 async

1
2
3
4
5
6
7
8
9
10
11
12
Promise.retry = async function (fn, times = 3) {
while (true) {
console.log(times);
times--;
try {
const res = await fn();
return res;
} catch (error) {
if (times === 0) throw error;
}
}
};

在内层使用 async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.retry = function (promiseFn, times = 3) {
return new Promise(async (resolve, reject) => {
while (true) {
console.log(times);
times--;
try {
var ret = await promiseFn();
resolve(ret);
break;
} catch (error) {
if (times === 0) {
reject(error);
break;
}
}
}
});
};

使用 promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.retry = function (fn, times = 3) {
return new Promise(function (resolve, reject) {
function retry() {
console.log(times);
times--;
fn()
.then(resolve)
.catch((error) => {
if (times === 0) {
reject(error);
} else {
retry();
}
});
}
retry();
});
};

参考资料

  1. 看了就会,手写 Promise 原理,最通俗易懂的版本