Promsie
学习文档
- 使用 Promise - MDN
- Promise - MDN
- Promise 对象 - ECMAScript 6 入门 - 阮一峰
- async 函数 - ECMAScript 6 入门 - 阮一峰
Promise原理/手写
输出类题目
代码实现
普通功能
javascript
const p = () =>
new Promise((resolve, reject) => {
const random = Math.random() * 1000
setTimeout(() => {
if (random > 200) {
reject('p 失败')
} else {
resolve('p 成功')
}
}, random)
})
//重试
function withRetry(max = 3) {
return new Promise((resolve, reject) => {
let count = 1
const excute = async () => {
try {
const res = await p()
console.log(res)
resolve(res)
} catch (error) {
console.log(error)
if (count === max) {
reject(error)
} else {
count++
excute()
}
}
}
excute()
})
}
;(async () => {
try {
const res = await withRetry()
console.log('retry result', res)
} catch (e) {
console.log('retry error', e)
}
})()
//超时
function withTimeout(t = 30000) {
const timout = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('超时了')
}, t)
})
}
return Promise.race([timout(), p()])
.then(res => Promise.resolve(res))
.catch(e => Promise.reject(e))
}
;(async () => {
try {
const res = await withTimeout()
console.log('timout result', res)
} catch (error) {
console.log('timout error ', error)
}
})()
//取消
function withCancel(p, cancle) {
return Promise.race([p(), cancle()])
.then(r => Promise.resolve(r))
.catch(e => Promise.reject(e))
}
;(async () => {
const cancel = () =>
new Promise((resolve, reject) => {
reject('取消')
})
try {
const res = await withCancel(p, cancel)
console.log('cancel result', res)
} catch (e) {
console.log('cancel error ', e)
}
})()
限制并发
题目
- 实现一个批量请求函数, 能够限制并发量?
- 使用 Promise 实现一个异步流量控制的函数, 比如一共 10 个请求, 每个请求的快慢不同, 最多同时 3 个请求发出, 快的一个请求返回后, 就从剩下的 7 个请求里再找一个放进请求池里, 如此循环。
- 好多请求, 耗时不同, 按照顺序输出, 尽可能保证快, 写一个函数。
js
const promiseList = [
new Promise(resolve => {
setTimeout(resolve, 1000)
}),
new Promise(resolve => {
setTimeout(resolve, 1000)
}),
new Promise(resolve => {
setTimeout(resolve, 1000)
}),
]
fn(promiseList)
all、allSettled
ts
//!!!注意这里与下面的区别,这里立即执行了,有没有 all,allSettled都会执行
let p1 = new Promise(resolve => {
setTimeout(() => {
console.log(1000)
resolve(1000)
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(2000)
// resolve(2000)
reject('error')
}, 1000)
})
let p3 = new Promise(resolve => {
setTimeout(() => {
console.log(3000)
resolve(3000)
}, 2000)
})
let p4 = new Promise(resolve => {
setTimeout(() => {
console.log(4000)
resolve(4000)
}, 1000)
})
let p5 = new Promise(resolve => {
setTimeout(() => {
console.log(5000)
resolve(5000)
}, 1000)
})
const ps = [p1, p2, p3, p4, p5]
Promise.allSettled(ps)
.then(r => console.log('allSettled then', r))
.catch(e => console.log('allSettled error', e))
Promise.all(ps.slice(2))
.then(r => console.log('all 3个 then', r))
.catch(e => console.log('all 3个 error', e))
//all 碰到一个error,立即返回
Promise.all(ps)
.then(r => console.log('all 5个 then', r))
.catch(e => console.log('all 5个 error', e))
limit
ts
const ps2 = []
for (let i = 0; i < 5; i++) {
const randomTime = Math.random() * 1000
//!!!注意这里与上面的区别,这里没有立即执行,是一个函数
const promise = () =>
new Promise(resolve => {
setTimeout(() => {
const msg = (i + 1) * 1000
console.log('limist promise', msg, randomTime)
resolve(msg)
}, randomTime)
})
ps2.push(promise)
}
function limitPromise(promiseList: any[], max: number) {
const len = promiseList.length
const res = new Array(len).fill(false)
let count = 0
return new Promise(resolve => {
const excute = (i: number) => {
promiseList[i]()
.then(r => (res[i] = r))
.catch(e => (res[i] = e))
.finally(() => {
if (count < len) {
excute(count++)
} else if (!res.includes(false)) {
resolve(res)
}
})
}
while (count < max) {
excute(count++)
}
})
}
limitPromise(ps2, 2)
.then(res => {
console.log('limitLoad then', res)
})
.catch(err => {
console.error('limitLoad catch', err)
})
图片加载
题目
- 使用 Promise 实现:限制异步操作的并发个数,并尽可能快的完成全部。
- 有多个图片资源的 url,已经存储在数组 urls 中,而且已经有一个函数 function loadImg,输入一个 url 链接,返回一个 Promise,该 Promise 在图片下载完成的时候 resolve,下载失败则 reject。
- 但有一个要求,任何时刻同时下载的链接数量不可以超过 3 个。
- 请写一段代码实现这个需求,要求尽可能快速地将所有图片下载完成。
ts
const urls = [
'https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF',
'https://t7.baidu.com/it/u=1297102096,3476971300&fm=193&f=GIF',
'https://t7.baidu.com/it/u=737555197,308540855&fm=193&f=GIF',
'https://t7.baidu.com/it/u=3195384123,421318755&fm=193&f=GIF222',
'https://t7.baidu.com/it/u=3078295664,4026667001&fm=193&f=GIF',
'https://t7.baidu.com/it/u=2790759587,1933417440&fm=193&f=GIF',
]
function loadImg(url: string) {
return new Promise((resolve, reject) => {
const randomTime = Math.random() * 1000
const img = new Image()
img.onload = function () {
setTimeout(() => {
console.log('一张图片加载完成 ' + url)
resolve(img)
}, randomTime)
}
img.onerror = function () {
reject(new Error('Could not load image at' + url))
}
img.src = url
})
}
loadImages(urls).then(res => {
console.log('加载图片结束 then', res)
})
function loadImages(urls: string[], limit = 3) {
const len = urls.length
const res = new Array(len).fill(false)
let count = 0
return new Promise(resolve => {
const excute = (i: number) => {
loadImg(urls[i])
.then(r => (res[i] = r))
.catch(e => (res[i] = e))
.finally(() => {
if (count < len) {
excute(count++)
} else if (!res.includes(false)) {
resolve(res)
}
})
}
while (count < limit) {
excute(count++)
}
})
}
Sleep函数
- JavaScript 中实现 sleep 睡眠函数的几种简单方法 - CSDN文章
ts
function sleep(delay: number) {
return new Promise(resolve => {
setTimeout(resolve, delay)
})
}
;(async function () {
console.log('1')
await sleep(1000)
console.log('2')
await sleep(2000)
console.log('3')
await sleep(3000)
})()
async function run() {
console.log('run 1')
await sleep(1000)
console.log('run 2')
await sleep(2000)
console.log('run 3')
await sleep(3000)
}
run()
console.log('11')
sleep(1000).then(() => {
console.log('22')
sleep(2000).then(() => {
console.log('33')
})
})
间隔输出
ts
function createRepeat(fn, repeat: number, interval: number) {
function sleep() {
return new Promise(resolve => {
setTimeout(resolve, interval * 1000)
})
}
return async function (...args) {
fn.apply(this, args)
for (let i = 1; i < repeat; i++) {
await sleep()
fn.apply(this, args)
}
}
}
const fn = createRepeat(console.log, 3, 4)
fn('helloWorld') // 每4秒输出一次helloWorld, 输出3次
链式调用
js
const boy = new PlayBoy('Tom')
boy.sayHi().sleep(1000).play('王者').sleep(2000).play('跳一跳')
// 输出
// 大家好我是Tom
// 1s 之后
// 我在玩王者
// 2s 之后
// 我在玩跳一跳
ts
class PlayBoy {
name = ''
queue = []
constructor(name: string) {
this.name = name
setTimeout(() => {
this.next()
}, 0)
return this
}
next() {
const fn = this.queue.shift()
fn && fn()
}
sleep(time: number) {
const fn = async () => {
await new Promise(resolve => {
setTimeout(resolve, time)
})
this.next()
}
// const fn = () => {
// setTimeout(() => {
// this.next()
// }, time)
// }
this.queue.push(fn)
return this
}
sayHi() {
const fn = () => {
console.log(`hi,我是${this.name}`)
this.next()
}
this.queue.push(fn)
return this
}
play(str: string) {
const fn = () => {
console.log(str)
this.next()
}
this.queue.push(fn)
return this
}
}
const boy = new PlayBoy('Tom')
boy.sayHi().sleep(1000).play('王者').sleep(2000).play('跳一跳')
红绿灯
使用睡眠函数实现红绿灯代码,红灯 2 秒,黄灯 1 秒,绿灯 3 秒,循环改变颜色。
ts
class TrafficLight {
timer
currentIndex = 0
lights = [
{ color: 'red', duration: 2000 },
{ color: 'yellow', duration: 1000 },
{ color: 'green', duration: 3000 },
]
constructor() {
this.changLight()
}
changLight() {
const { color, duration } = this.lights[this.currentIndex]
console.log('demo1', color)
this.timer = setTimeout(() => {
this.currentIndex++
if (this.currentIndex === this.lights.length) {
this.currentIndex = 0
}
this.changLight()
}, duration)
}
stop() {
clearTimeout(this.timer)
}
}
const lights = new TrafficLight()
//取消
onBeforeUnmount(() => {
lights.stop()
})
ts
function lightOn(delay: number) {
return new Promise(resolve => {
setTimeout(() => {
resolve('')
}, delay)
})
}
async function trafficLight(lights: Lights) {
// eslint-disable-next-line no-constant-condition
while (true) {
for (let i = 0; i < lights.length; i++) {
const { color, time } = lights[i]
console.log('demo2', color)
await lightOn(time)
}
}
}
trafficLight([
{ color: 'red', time: 1000 },
{ color: 'green', time: 2000 },
{ color: 'yellow', time: 3000 },
])
- 用 React 实现一个信号灯(交通灯)控制器 - GitHub
实现一个信号灯(交通灯)控制器,要求:
默认情况下,
- 红灯亮 20 秒,并且最后 5 秒闪烁
- 绿灯亮 20 秒,并且最后 5 秒闪烁
- 黄灯亮 10 秒
- 次序为 红-绿-黄-红-绿-黄
灯的个数、颜色、持续时间、闪烁时间、灯光次序都可配置,如: lights=[{color: '#fff', duration: 10000, twinkleDuration: 5000}, ... ]
ts
class TrafficLightWithTwinkle {
currentIndex = 0
lights = [
{ color: 'red', duration: 20, twinkleDuration: 5 },
{ color: 'green', duration: 20, twinkleDuration: 5 },
{ color: 'yellow', duration: 10, twinkleDuration: 0 },
]
constructor() {
this.changeLight()
}
wait(duration: number) {
return new Promise(resolve => {
setTimeout(resolve, duration * 1000)
})
}
async changeLight() {
const { color, duration, twinkleDuration } = this.lights[this.currentIndex]
console.log(`${color} 亮 ${duration}秒`)
await this.wait(duration)
if (twinkleDuration > 0) {
console.log(`${color} 闪烁 ${twinkleDuration}秒`)
await this.wait(twinkleDuration)
}
this.currentIndex++
if (this.currentIndex === 3) {
this.currentIndex = 0
}
this.changeLight()
}
}
const tr = new TrafficLightWithTwinkle()
todo
取消