你说你会Promise?那你解决一下项目中的这五个难题?
theme: smartblue
前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心,众所周知哈,Promise
在咱们的开发中是相当的重要,我觉得对于Promise
的使用等级,可以分为三个等级
- 1、掌握Promise
的基本使用
- 2、掌握Promise
的基本原理
- 3、在项目中能灵活运用Promise
解决一些问题
第一点的话,其实就是能掌握Promise
的一些基本使用方法以及一些方法,如then、catch、all、race、finally、allSettled、any、resolve
等等
第二点的话,就是要能简单实现一下Promise
的原理,这能使我们对Promise
的那些常用方法有更好的理解
第三点的话,就是要能灵活Promise
解决咱们开发中的一些问题,今天我就给大家说一下我用Promise
在项目开发中解决了什么问题吧!
接口请求超时
顾名思义,就是给定一个时间,如果接口请求超过这个时间的话就报错
1、自己实现
实现思路就是:接口请求
和延时函数
赛跑,并使用一个Promise
包着,由于Promise
的状态是不可逆的,所以如果接口请求
先跑完则说明未超时
且Promise
的状态是fulfilled
,反之,延时函数
先跑完则说明超时了
且Promise
的状态是rejetced
,最后根据Promise
的状态来判断有无超时
```js
/*
* 模拟延时
* @param {number} delay 延迟时间
* @returns {Promise
/* * 模拟请求 / function request() { // 假设请求需要 1s return new Promise(resolve => { setTimeout(() => resolve('成功喽'), 1000) }) }
/*
* 判断是否超时
* @param {() => Promise
2、Promise.race
其实timeoutPromise
中的代码可以使用Promise.race
来代替,是同样的效果
js
function timeoutPromise(requestFn, delay) {
// 如果先返回的是延迟Promise则说明超时了
return Promise.race([requestFn(), sleep(delay)])
}
3、测试
```js // 超时 timeoutPromise(request, 500).catch(err => console.log(err)) // 超时喽
// 不超时 timeoutPromise(request, 2000).then(res => console.log(res)) // 成功喽 ```
转盘抽奖
我们平时在转盘抽奖时,一般都是开始转动的同时也发起接口请求,所以有两种可能 - 1、转盘转完,接口还没请求回来,这是不正常的
- 2、转盘转完前,接口就请求完毕,这是正常的,但是需要保证
请求回调
跟转盘转完回调
同时执行
1、转盘转完,接口还没请求回来
主要问题就是,怎么判断接口请求时间
是否超过转盘转完所需时间
,咱们其实可以用到上一个知识点接口请求超时
,都是一样的道理。如果转盘转完所需时间
是2500ms
,那咱们可以限定接口请求
需要提前1000ms
请求回来,也就是接口请求
的超时时间为2500ms - 1000ms = 1500ms
```js
/*
* 模拟延时
* @param {number} delay 延迟时间
* @returns {Promise
/* * 模拟请求 / function request() { return new Promise(resolve => { setTimeout(() => resolve('成功喽'), 1000) }) }
/*
* 判断是否超时
* @param {() => Promise
2、转盘转完前,接口就请求完毕
咱们确保了接口请求
可以在转盘转完
之前请求回来,但是还有一个问题,就是需要保证请求回调
跟转盘转完回调
同时执行,因为虽然接口请求
请求回来的时候,转盘还在转着,咱们需要等转盘转完时,再一起执行这两个回调
听到这个描述,相信很多同学就会想到Promise.all
这个方法
```js // ...上面代码
/*
* 模拟转盘旋转到停止的延时
* @param {number} delay 延迟时间
* @returns {Promise
/*
* 判断是否超时
* @param {() => Promise
function zhuanpanPromise(requsetFn, turntableDelay, delay) { return Promise.all([timeoutPromise(requsetFn, delay), turntableSleep(turntableDelay)]) }
```
3、测试
js
// 不超时,且先于转盘停止前请求回数据
zhuanpanPromise(request, 2500, 1500).then(res => console.log(res), err => console.log(err))
控制并发的Promise的调度器
想象一下,有一天你突然一次性发了10个请求,但是这样的话并发量是很大的,能不能控制一下,就是一次只发2个请求,某一个请求完了,就让第3个补上,又请求完了,让第4个补上,以此类推,让最高并发量变成可控的
```js 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
```
实现
```js class Scheduler { constructor(limit) { this.queue = [] this.limit = limit this.count = 0 }
add(time, order) { const promiseCreator = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log(order) resolve() }, time) }) } this.queue.push(promiseCreator) }
taskStart() { for(let i = 0; i < this.limit; i++) { this.request() } }
request() { if (!this.queue.length || this.count >= this.limit) return this.count++ this.queue.shift()().then(() => { this.count-- this.request() }) } } ```
测试
js
// 测试
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();
取消重复请求
举个例子,咱们在做表单提交时,为了防止多次重复的提交,肯定会给按钮的点击事件加上防抖措施
,这确实是有效地避免了多次点击造成的重复请求,但是其实还是有弊端的
众所周知,为了用户更好地体验,防抖
的延时是不能太长的,一般在我的项目中都是300ms
,但是这只能管到请求时间 < 300ms
的接口请求,如果有一个接口请求需要2000ms
,那么此时防抖
也做不到完全限制重复请求
,所以咱们需要额外做一下取消重复请求
的处理
实现
实现思路:简单说就是,利用Promise.race
方法,给每一次请求的身边安装一颗雷,如果第一次请求后,又接了第二次重复请求,那么就执行第一次请求身边的雷,把第一次请求给炸掉,以此类推。
```js class CancelablePromise { constructor() { this.pendingPromise = null this.reject = null }
request(requestFn) { if (this.pendingPromise) { this.cancel('取消重复请求') }
const promise = new Promise((_, reject) => (this.reject = reject))
this.pendingPromise = Promise.race([requestFn(), promise])
return this.pendingPromise
}
cancel(reason) { this.reject(reason) this.pendingPromise = null } }
function request(delay) { return () => new Promise(resolve => { setTimeout(() => { resolve('最后赢家是我') }, delay) }) } ```
测试
```js const cancelPromise = new CancelablePromise()
// 模拟频繁请求5次 for (let i = 0; i < 5; i++) { cancelPromise .request(request(2000)) .then((res) => console.log(res)) // 最后一个 最后赢家是我 .catch((err) => console.error(err)); // 前四个 取消重复请求 } ```
全局请求loading
比如一个页面中,或者多个组件中都需要请求并且展示loading状态
,此时我们不想要每个页面或者组件都写一遍loading
,那我们可以统一管理loading
,loading
有两种情况
- 1、全局只要有一个接口还在请求中,就展示loading
- 2、全局所有接口都不在请求中,就隐藏loading
那我们怎么才能知道全局接口的请求状态呢?其实咱们可以利用Promise
,只要某个接口请求Promise
的状态不是pending
那就说明他请求完成了,无论请求成功或者失败,既然是无论成功失败,那咱们就会想到Promise.prototype.finally
这个方法
实现
```js class PromiseManager { constructor() { this.pendingPromise = new Set() this.loading = false }
generateKey() {
return ${new Date().getTime()}-${parseInt(Math.random() * 1000)}
}
push(...requestFns) { for (const requestFn of requestFns) { const key = this.generateKey() this.pendingPromise.add(key) requestFn().finally(() => { this.pendingPromise.delete(key) this.loading = this.pendingPromise.size !== 0 }) } } } ```
测试
```js // 模拟请求 function request(delay) { return () => { return new Promise(resolve => { setTimeout(() => resolve('成功喽'), delay) }) } }
const manager = new PromiseManager()
manager.push(request(1000), request(2000), request(800), request(2000), request(1500))
const timer = setInterval(() => { // 轮询查看loading状态 console.log(manager.loading) }, 300) ```
参考
结语
如果你觉得此文对你有一丁点帮助,点个赞,鼓励一下林三心哈哈。或者可以加入我的摸鱼群 想进学习群,摸鱼群,请点击这里摸鱼,我会定时直播模拟面试,简历指导,答疑解惑,目前已面试过50+位同学
- 监听localStorage的变化?小问题啊~
- 你敢信?区区一个Input标签让我抓破头皮~
- 每个前端都必须要学会的Webpack优化手段
- 太震撼了!我把七大JS排序算法做成了可视化!!!太好玩了!
- 「自我检验」熬夜总结50个Vue知识点,全都会你就是神!!!
- 所以,e.target 和 e.currentTarget 到底有啥区别呢?
- async/await 你是会用,但是你知道怎么处理错误吗?
- 你前端都只是半桶水而已,你还敢问我怎么学后端?
- 春节走亲戚不知道该怎么称呼?”亲戚关系计算器“你值得拥有!
- 从3000人大公司跳到30人小公司的转变|2021 年中总结
- 「前端总结」林三心把今年写的55篇前端文章画成了一张图
- 想知道一个20k级别前端在项目中是怎么使用LocalStorage的吗?
- 你说你会Promise?那你解决一下项目中的这五个难题?
- 后端一次给你10万条数据,如何优雅展示,到底考察我什么?
- 网易面试官:请你实现一下JS重载?可不是TS重载哦!
- 看似简单的题,席卷几十个前端群,王红元老师都亲自出面解答
- 史上最全!熬夜整理56个JavaScript高级的手写知识点!!专业扫盲!
- 「万字总结」熬夜总结50个JS的高级知识点,全都会你就是神!!!
- 工作中遇到的50个JavaScript的基础知识点,满分找我拿奖品!
- leader:深拷贝有这5个段位,你只是青铜段位?还想涨薪?