主页
主页
文章目录
  1. promise
  2. promise主体
    1. 1.执行器executor在new Promise时是立即执行的。
    2. 2. 规范中规定: 如果一旦状态改变为成功或失败,就不能再变回失败或成功了
    3. 3. 在使用中不关使用reject()可以抛出错误,直接使用throw new Error也可以进入rejected逻辑
    4. 4. 如果执行器中有异步操作。则调用then时有可能还是pendding状态。
    5. 5. 如果then中传入的不是函数
    6. 6. 如果resolve或者reject一个promise
    7. 基本架构

promise上

promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。

promise用法可以参考:promise用法
promise/A+ 规范: promise/A+
promise/A+中文翻译: promise/A+中文

简单的使用就不在探究了。可以自行查看文档。这里只把使用上表现结合起来探究其原理,并手写一个Promise

promise主体

根据promise/A+规范

  1. promise是一个类。
  2. 每次new一个Promise时都需要传递一个执行器。是立即执行的
  3. 执行器中有两个参数resolve、reject
  4. 默认promise中有是三个状态,pendding、fulfilled、rejected
    pendding -> resolve 就代表成功fulfilled
    pendding -> reject 就代表成功rejected
  5. 如果一旦状态改变为成功或失败,就不能再变回失败或成功了
  6. 每个promise都有一个then方法

先根据如下规范可以写出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
28
29
30
31
32
33
34
35
36
37
38
 const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class Promise {
constructor(executor) {
// 保存成功后的信息 resolve(222) -> 222
this.value = undefined;
// 保存失败后的错误 reject(333) -> 333
this.reason = undefined;
// 保存promise状态
this.status = PENDING;
// 调用resolve()时的回调
let resolve = value => {
this.value = value;
this.status = FULFILLED;
}
// 调用reject()时的回调
let resolve = reason => {
this.reason = reason;
this.status = REJECTED;
}
// new Promise(executor) 立即执行执行器,传入回调
executor(resolve, reject);
}

// 外界调用的then函数
then(onFulfilled, onRejected) {
// 当状态为成功时,调用p.then((res) => {})传入的第一个回调
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
// 当状态为成功时,调用p.then(null, (res) => {})传入的第二个回调
if (this.status === REJECTED) {
onRejected(this.reason);
}
}
}

1.执行器executor在new Promise时是立即执行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
new Promise((resolve, reject) => {
console.log(1)
})
console.log(2)
// 1
// 2

new Promise((resolve, reject) => {
setTimeout(() => {console.log(1)})
})
console.log(2)
// 2
// 1

2. 规范中规定: 如果一旦状态改变为成功或失败,就不能再变回失败或成功了

所以代码需要改写,增加判断当前状态是不是PENDDING,如果是才能修改状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 调用resolve()时的回调
let resolve = value => {
if(this.status === PENDDING) {
this.value = value;
this.status = FULFILLED;
}
}
// 调用reject()时的回调
let resolve = reason => {
if(this.status === PENDDING) {
this.reason= reason;
this.status = REJECTED;
}
}

3. 在使用中不关使用reject()可以抛出错误,直接使用throw new Error也可以进入rejected逻辑

1
2
3
let p = new Promise((resolve)=>{throw new Error(666)})
p.then(null, err => {console.log(err)})
// vendor.7ab21e702e7733b6b702.js:1 Error: 666

所以需要在调用执行器时加入try… catch

1
2
3
4
5
6
7
// new Promise(executor) 立即执行执行器,传入回调
try {
executor(resolve, reject);
} catch(e) {
// 如果有throw的错误,直接执行reject回调
reject(e);
}

4. 如果执行器中有异步操作。则调用then时有可能还是pendding状态。

1
2
3
4
5
6
let p = new Promise(resolve => {
setTimeout(() => {
resolve(666)
})
// 或者ajax请求等异步操作
});

所以需要增加逻辑判断。并利用发布订阅。在构造函数中定义onResolvedCallbacks和onRejectedCallbacks队列,如果是pendding状态则将回调push入队列中。在异步执行完毕调用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
constructor {
// 再定义两个队列
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];

// 调用resolve()时的回调

let resolve = value => {
if(this.status === PENDDING) {
this.value = value;
this.status = FULFILLED;
// 一旦调用resolve() 就把之前保存的异步回调都执行一遍
this.onResolvedCallbacks.forEach(fn => fn());
}
}
// 调用reject()时的回调
let resolve = reason => {
if(this.status === PENDDING) {
this.reason= reason;
this.status = REJECTED;
// 一旦调用reject() 就把之前保存的异步回调都执行一遍
this.onRejectedCallbacks.forEach(fn => fn());
}
}
}
then(onFulfilled, onRejected) {
if (this.status === PENDDING) {
this.onResolvedCallbacks.push(() => {
// todo
onFulfilled(this.value);
})
this.onRejectedCallbacks.push(() => {
// todo
onRejected(this.reason);
})
}
}

回调为什么要定义成队列数组呢?
因为一个promise实例有可能有多个then调用。

1
2
3
4
5
6
7
let p = new Promise(resolve => {resolve(43)});

p.then(res => {console.log(1)});
p.then(res => {console.log(2)});
p.then(res => {console.log(3)});

// 上面一个实例p,有三个then函数调用。所以会把他的onFulfilled回调都放入数组中

5. 如果then中传入的不是函数

如果then中传入的不是函数,不会抛出错误,而会继续略过他继续向下传递

1
2
3
let p = new Promise(resolve => resolve(43));
p.then(undefined, 443).then(res => {console.log(res)})
// 43

所以在then方法中需要添加容错处理。

1
2
3
4
5
then(onFulfilled, onRejected) {
// 如果成功回调不是函数则将value继续向下传递,失败则将reason继续向后抛
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err};
}

then函数中的回调会放入微任务中。在下次eventLoop中执行。

1
2
3
4
5
6
7
8
9
10
11
let p = new Promise(resolve => resolve());
// 如果传入不是函数继续向下抛。则会再多执行一次then。需要2次eventLoop
p.then(2323, false).then(res => {console.log(1)});
// 放入微任务 需要1次eventLoop
p.then(res => {console.log(2)});
// 直接执行
console.log(3)

// 3
// 2
// 1

6. 如果resolve或者reject一个promise

如果是resolve(new Promise())的话,就用这个promise的状态作为下个then的状态。
如果是reject(new Promise())的话,不需要执行直接返回。

1
2
3
4
5
6
7
8
9
10
new Promise(resolve => {
resolve(new Promise(resovle => resovle(1)))
}).then((res) => {
console.log(res); // 1
})
new Promise((resolve, reject) => {
reject(new Promise(resovle => resovle(1)))
}).then(null, (res) => {
console.log(res); // Promise {<resolved>: 1}
})

需要在成功的resolve()回调中增加判断

1
2
3
4
5
6
7
8
9
10
11
12
const resolve = value => {
// 如果resolve()参数传入的是个promise,直接执行它的then方法。
// 需要等待他执行完毕,拿到他的状态并且返回一个新的promise
if (value instanceOf Promise) {
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}

实例:

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
new Promise(resolve => {
resolve(new Promise(resovle => resovle(1)))
}).then(() => {
console.log("tick 3");
}).then(() => {
console.log('tick 4')
});

new Promise(resovle => resovle(1)).then(() => {
console.log("tick 1");
}).then(() => {
console.log("tick 2");
});
// 1 2 3 4

// 因为resolve()一个promise需要等待他执行完毕,并且then方法返回一个新的promise。
// 相当于如下操作:
new Promise(resolve => resolve(1)).then(res => {
// 调用resolve中promise的then,返回一个新的promise。
// 这个promise的resolve值已经是普通值了。如果继续嵌套那就继续递归。
// 如果then中return promise 还是会等待他执行完then方法的。
return new Promise(resolve => resolve(res));
}).then((res) => {
console.log("tick 3",res);
}).then((res) => {console.log('tick 4', 1)});

// 也就相当于多了两次then。
new Promise(resolve => resolve(1)).then(res => res).then(res => res).then(res => {
console.log('tick 3')
}).then(() => {
console.log('tick 4')
});

基本架构

所以根据上述promise/A+规范。写出最基本的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
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
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
constructor (executor) {
this.value = undefined;
this.reason = undefined;
this.status = PENDING;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];

const resolve = value => {
if (value instanceOf Promise) {
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
const reject = reason => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach(fn => fn());
}
}

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

then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err};

if (this.status === FULFILLED) {
onFulfilled(this.value);
}

if (this.status === REJECTED) {
onRejected(this.reason);
}

if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
})
}
}
}
支持一下
扫一扫,支持superkimi
  • 微信扫一扫
  • 支付宝扫一扫