Preface
Hello, everyone, I’m Lin Sanxin, in the most easy-to-understand terms, is my motto, based on the premise of advanced is my original intention, as Promise
in our development It is very important. I think there are three levels for the use level of Promise
- 1. Master the basic usage
Promise
- 2. Master the basic principles
Promise
- 3. In the project,
Promise
solve some problems
The first point, then, in fact, be able to grasp Promise
some of the basic methods of use and methods, such as then、catch、all、race、finally、allSettled、any、resolve
etc.
The second point, then, is simply to be able to achieve what Promise
principle, which enables us to Promise
have a better understanding of those common method
The third point, then, is to be flexible Promise
solve some of the problems in our development, and today I'll tell you that what I Promise
solve the problem in project development it!
Interface request timed out
As the name implies, given a time, if the interface request exceeds this time, an error will be reported
1. Realize by yourself
The idea is to achieve: interface request and
delay function race, and the use of a
Promise
wrapped, because Promise
state is not reversible, so if interface request to the finish described
expires and
Promise
state is fulfilled
, and vice versa , delay function is described to finish
timeout and
Promise
state is rejetced
, according to the last Promise
determine the status of the presence or absence of timeout
/**
* 模拟延时
* @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
In fact, the code timeoutPromise
Promise.race
, which has the same effect
function timeoutPromise(requestFn, delay) {
// 如果先返回的是延迟Promise则说明超时了
return Promise.race([requestFn(), sleep(delay)])
}
3. Test
// 超时
timeoutPromise(request, 500).catch(err => console.log(err)) // 超时喽
// 不超时
timeoutPromise(request, 2000).then(res => console.log(res)) // 成功喽
Turntable lottery
When we usually draw a lottery on the turntable, we usually start to spin and initiate an interface request at the same time, so there are two possibilities
- 1. After the turntable is turned, the interface has not requested to come back, which is abnormal
- 2. The interface request is completed before the turntable is finished. This is normal, but it is necessary to ensure that the
request callback and the
turntable callback are executed at the same time.
1. After the turntable is turned, the interface has not requested to come back
The main problem is how to judge whether the request time of the interface exceeds the
turntable to complete the turntable. In fact, we can use the previous knowledge point
interface request timeout, all of which are the same. If
rotary table is the time required for complete
2500ms
, that we can define interface request in advance
1000ms
request back, i.e. timeout interface request is
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. The interface request is completed before the turntable is finished
Let's make sure the interface request can
request back before the rotary table finish, but there is a problem, is the need to ensure
request a callback with
rotary table complete the callback is executed at the same time, because although
time interface request request back, still turntable Turn, we need to wait for the turntable to finish before executing these two callbacks together
Hearing this description, I believe many students will think of the method 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. Test
// 不超时,且先于转盘停止前请求回数据
zhuanpanPromise(request, 2500, 1500).then(res => console.log(res), err => console.log(err))
Scheduler that controls concurrent Promise
Imagine that one day you suddenly send 10 requests at a time, but in this case the amount of concurrency is very large, can you control it, that is, only send 2 requests at a time, and let the third one fill up when one request is over. , The request is over again, let the fourth one fill up, and so on, so that the highest concurrency becomes controllable
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
accomplish
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()
})
}
}
test
// 测试
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();
Cancel duplicate request
For example, when we are doing form submission, in order to prevent multiple repeated submissions, we will definitely add anti-shake measures to the click event of the button. This is indeed effective in avoiding repeated requests caused by multiple clicks, but in fact There are still drawbacks
As we all know, for a better user experience, the anti-shake cannot be too long. Generally
300ms
in my project, but this can only with the interface request with 161a821a4b4011 request time <300ms. If there is an interface request Need
2000ms
, then anti-shake can not completely limit
repeated requests, so we need to do an extra
cancel the processing of repeated requests
accomplish
Realization idea: Simply put, use the Promise.race
method to install a mine next to each request. If the second request is received after the first request, then the mine next to the first request will be executed. The first request was bombed, and so on.
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)
})
}
test
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)); // 前四个 取消重复请求
}
Global request loading
For example, a page or more of the components are need to request and display loading state, at this time we do not want every page or component are written again
loading
, then we can be unified management loading
, loading
there are two cases
- 1. As long as there is an interface still in the global request, it will show
loading
- 2. If all interfaces in the world are not in the request, then
loading
Then how can we know the request status of the global interface? In fact, we can use Promise
. As long as the Promise status of interface request
pending
it means that his request is completed. No matter whether the request succeeds or fails, since it is a success or failure, then we will think of the method Promise.prototype.finally
accomplish
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
})
}
}
}
test
// 模拟请求
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)
refer to
Concluding remarks
If you think this article is of little help to you, please give me a thumbs up and encourage Lin Sanxin haha. Or you can join my fish-fishing school. Let's study together, ah, ah, ah, ah, I will mock interviews regularly, resume guidance, answer questions, and we will learn from each other and make progress together! !
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。