Promise
面试相关的Promsie
是重点中的重点,首先我们先从手写简易的promise
入手,之后再详细处理各种业务场景
先来看看Promise
的用法
const promise = new Promise((resolve, reject) => {
// 异步执行成功
resolve()
// 异步执行失败
reject()
})
promise.then(res => {
// 触发成功回调
}, err => {
// 触发失败回调
}).then(res => {
// 第一个then 返回promise 可以链式调用
}).catch(err => {
// 捕获错误
})
// 静态方法
Promsie.all()
Promsie.race()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
初步实现
- promise存在三种状态,
pending
,fulfilled
,reject
then
方法处理两种回调
class MyPromise {
constructor(fn) {
// 表示状态
this.state = 'pending'
// 表示then注册的成功函数
this.successFn = []
// 表示then注册失败的函数
this.failFn = []
let resolve = (val) => {
// 如果状态改变 promise就不能再次触发
if (this.state !== 'pending') return
// 成功触发则改变为成功状态
this.state = 'success'
// 为了确保执行顺序 需要异步
setTimeout(() => {
this.successFn.forEach(item => item.call(this, val))
}, 0)
}
let reject = (err) => {
// 如果状态改变 promise就不能再次触发
if (this.state !== 'pending') return
// 成功触发则改变为失败状态
this.state = 'fail'
setTimeout(() => {
this.failFn.forEach((item) => item.call(this, err))
})
}
// 调用函数
try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
// 实例方法 then
then (resolveCallback, rejectCallback) {
// 判断回调是否是函数
resolveCallback = typeof resolveCallback !== 'function' ? (v) => v : resolveCallback
rejectCallback = typeof rejectCallback !== 'function' ? err => { throw err } : rejectCallback
// 为了保持链式调用 继续返回 promise
return new MyPromise((resolve, reject) => {
// 将回调注册到 successfn中,注意这里不是执行,执行在最后的resolve
this.successFn.push(val => {
try {
// 在这里执行回调
let x = resolveCallback(val)
//(最难的一点)
// 如果回调函数结果是普通值 那么就resolve出去给下一个then链式调用 如果是一个promise对象(代表又是一个异步) 那么调用x的then方法 将resolve和reject传进去 等到x内部的异步 执行完毕的时候(状态完成)就会自动执行传入的resolve 这样就控制了链式调用的顺序
x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
})
this.failFun.push((val) => {
try {
// 执行回调函数
let x = rejectCallback(val)
x instanceof MyPromise ? x.then(resolve, reject) : reject(x)
} catch (error) {
reject(error)
}
})
})
}
}
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
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
实现all和race
class MyPromise {
static all (promiseArr) {
let result = []
// 声明一个计数器,每一个promise返回就加1
let count = 0
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
// 这里 promise.resolve包装一下, 防止不是promise的类型传进来
Promise.resolve(promiseArr[i]).then(
(res) => {
//这里不能直接push数组 因为要控制顺序一一对应
result[i] = res
count++
// 只有全部的promise执行成功才resolve
if (count === promiseArr.length) {
resolve(result)
}
},
err => {
reject(err)
}
)
}
})
}
static race (promiseArr) {
return new MyPromise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then(res => {
//promise数组只要有任何一个promise 状态变更 就可以返回
resolve(res)
}, err => {
reject(err)
})
}
})
}
}
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
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
这样基本的promise
就实现完了,它只是基本的并不完全符合A+
规范,只是让我们对promise
有个在实现上的了解。 最重要的是,要去看一些关于promise
在业务上的处理
promise
如何实现一个可取消的我们知道Promise.race
方法又一个返回成功就返回,那么就可以用它来实现
function wrap(pro) {
let obj = {}
p1 = new Promise((resolve, reject) => {
obj.resolve = resolve
obj.reject = reject
})
obj.promise = Promise.race(p1, pro)
return obj
}
let testPro = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123)
}, 1000);
})
// 取消
wrapPro.resolve('取消了')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
扩展 超过多长时间就取消
Promise.race([promise1,timeOutPromise(5000)]).then(res=>{})
1
重试
可以重试请求的request
function retryRequest (request, count) {
let retryCount = 0
return new Promise((resolve, reject) => {
const retryFunc = () => {
request().then(res => {
resolve(res)
}).catch(() => {
if (retryCount < count) {
retryCount++
console.log(`重试次数:${retryCount}`)
retryFunc()
} else {
retryCount = 0
reject()
}
})
}
retryFunc()
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
实现有并行限制的 promise 调度器
题目描述:JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个 addTask(1000,"1"); addTask(500,"2"); addTask(300,"3"); addTask(400,"4"); 的输出顺序是:2 3 1 4
整个的完整执行流程:
一开始1、2两个任务开始执行 500ms时,2任务执行完毕,输出2,任务3开始执行 800ms时,3任务执行完毕,输出3,任务4开始执行 1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行 1200ms时,4任务执行完毕,输出4
class Scheduler {
constructor(limit) {
this.queue = []
this.maxCount = limit
this.runCounts = 0
}
add(time, order) {
const promiseCreator = () => {
return new Promsie((resolve, reject) => {
setTimeout(() => {
console.log(order)
resolve()
}, time);
})
}
this.queue.push(promiseCreator)
}
taskStar() {
for(let i=0;i< this.maxCount; i++) {
this.request()
}
}
request() {
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount)
return
this.runCount++
this.queue.shift()().then(() => {
this.runCount--
this.request()
})
}
}
const scheduler = new Scheduler(2)
const addTask =(time, order) => {
scheduler.add(time, order)
}
addTask(1000, '1')
addTask(500, '2')
addTask(300, '3')
addTask(400, '4')
scheduler.taskStart()
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
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
红绿灯
题目:使用Promise实现红灯每隔3s亮一次,黄灯每隔2s亮一次,绿灯每隔1s亮一次,循环这个过程。
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
const task = (light, timer) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if(light ==='red') {
console.log('红灯亮')
} else if (light === 'yellow') {
console.log('黄灯亮')
} else {
console.log('绿灯亮')
}
resolve()
}, timer);
})
}
const taskLoop = () => {
// promise方案
task('red', 3000)
.then(() => task('yellow', 2000))
.then(() => task('green', 1000))
.then(taskLoop)
}
// async/await
// const taskLoop = async () => {
// await task(3000, 'red')
// await task(2000, 'green')
// await task(1000, 'yellow')
// taskLoop()
// }
taskLoop()
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
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