前言
大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,根底是进阶的前提是我的初心,家喻户晓哈, 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
的状态来判断有无超时
/** * 模仿延时 * @param {number} delay 延迟时间 * @returns {Promise<any>} */function sleep(delay) { return new Promise((_, reject) => { setTimeout(() => reject('超时喽'), delay) })}/** * 模仿申请 */function request() { // 假如申请须要 1s return new Promise(resolve => { setTimeout(() => resolve('胜利喽'), 1000) })}/** * 判断是否超时 * @param {() => Promise<any>} requestFn 申请函数 * @param {number} delay 提早时长 * @returns {Promise<any>} */function timeoutPromise(requestFn, delay) { return new Promise((resolve, reject) => { const promises = [requestFn(), sleep(delay)] for (const promise of promises) { // 超时则执行失败,不超时则执行胜利 promise.then(res => resolve(res), err => reject(err)) } })}
2、Promise.race
其实 timeoutPromise
中的代码能够应用 Promise.race
来代替,是同样的成果
function timeoutPromise(requestFn, delay) { // 如果先返回的是提早Promise则阐明超时了 return Promise.race([requestFn(), sleep(delay)])}
3、测试
// 超时timeoutPromise(request, 500).catch(err => console.log(err)) // 超时喽// 不超时timeoutPromise(request, 2000).then(res => console.log(res)) // 胜利喽
转盘抽奖
咱们平时在转盘抽奖时,个别都是开始转动的同时也发动接口申请,所以有两种可能
- 1、转盘转完,接口还没申请回来,这是不失常的
- 2、转盘转完前,接口就申请结束,这是失常的,然而须要保障
申请回调
跟转盘转完回调
同时执行
1、转盘转完,接口还没申请回来
次要问题就是,怎么判断 接口申请工夫
是否超过 转盘转完所需工夫
,咱们其实能够用到上一个知识点 接口申请超时
,都是一样的情理。如果 转盘转完所需工夫
是 2500ms
,那咱们能够限定 接口申请
须要提前 1000ms
申请回来,也就是 接口申请
的超时工夫为 2500ms - 1000ms = 1500ms
/** * 模仿延时 * @param {number} delay 延迟时间 * @returns {Promise<any>} */function sleep(delay) { return new Promise((_, reject) => { setTimeout(() => reject('超时喽'), delay) })}/** * 模仿申请 */function request() { return new Promise(resolve => { setTimeout(() => resolve('胜利喽'), 1000) })}/** * 判断是否超时 * @param {() => Promise<any>} requestFn 申请函数 * @param {number} delay 提早时长 * @returns {Promise<any>} */function timeoutPromise(requestFn, delay) { return Promise.race([requestFn(), sleep(delay)])}
2、转盘转完前,接口就申请结束
咱们确保了 接口申请
能够在 转盘转完
之前申请回来,然而还有一个问题,就是须要保障 申请回调
跟 转盘转完回调
同时执行,因为尽管 接口申请
申请回来的时候,转盘还在转着,咱们须要等转盘转完时,再一起执行这两个回调
听到这个形容,置信很多同学就会想到 Promise.all
这个办法
// ...下面代码/** * 模仿转盘旋转到进行的延时 * @param {number} delay 延迟时间 * @returns {Promise<any>} */ function turntableSleep(delay) { return new Promise(resolve => { setTimeout(() => resolve('进行转动喽'), delay) })}/** * 判断是否超时 * @param {() => Promise<any>} requestFn 申请函数 * @param {number} turntableDelay 转盘转多久 * @param {number} delay 申请超时时长 * @returns {Promise<any>} */function zhuanpanPromise(requsetFn, turntableDelay, delay) { return Promise.all([timeoutPromise(requsetFn, delay), turntableSleep(turntableDelay)])}
3、测试
// 不超时,且先于转盘进行前申请回数据zhuanpanPromise(request, 2500, 1500).then(res => console.log(res), err => console.log(err))
管制并发的Promise的调度器
设想一下,有一天你忽然一次性发了10个申请,然而这样的话并发量是很大的,能不能管制一下,就是一次只发2个申请,某一个申请完了,就让第3个补上,又申请完了,让第4个补上,以此类推,让最高并发量变成可控的
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.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() }) }}
测试
// 测试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
办法,给每一次申请的身边装置一颗雷,如果第一次申请后,又接了第二次反复申请,那么就执行第一次申请身边的雷,把第一次申请给炸掉,以此类推。
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) })}
测试
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
这个办法
实现
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 }) } }}
测试
// 模仿申请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)
参考
- Promise技术点-面试实战版
结语
如果你感觉此文对你有一丁点帮忙,点个赞,激励一下林三心哈哈。或者退出我的群哈哈,咱们一起摸鱼一起学习 : meron857287645