One drawback to promises is that once they are created, they cannot be cancelled, so in essence promises cannot be terminated, but we may encounter two requirements during development:
Interrupt the call chain
After a then/catch is executed, you don’t want to let subsequent chained calls continue, that is:
somePromise
.then((a)= > {})
.then((a)= > {
// Terminate the Promise chain so that the following then, catch, and finally are not executed
})
.then((a)= > console.log('then'))
.catch((a)= > console.log('catch'))
.finally((a)= > console.log('finally'))
Copy the code
The answer is to return a permanently pending promise in the last line of then/catch:
return new Promise((resolve, reject) = > {})
Copy the code
Then all subsequent then, catch, and finally will not be executed.
Break Promise
Note that this is an interrupt, not a termination, because promises cannot be terminated. This interrupt means that a Promise with a pending state is rejected when appropriate. For example, a common application scenario is to set a timeout period for a network request, once the timeout will be interrupted, here we use the timer to simulate a network request, random return within 3 seconds:
const request = new Promise((resolve, reject) = > {
setTimeout((a)= > {
resolve('Server data received')},Math.random() * 3000)})Copy the code
If you think that more than 2 seconds is a network timeout, you can write a wrapper for the promise function timeoutWrapper:
function timeoutWrapper(p, timeout = 2000) {
const wait = new Promise((resolve, reject) = > {
setTimeout((a)= > {
reject('Request timeout')
}, timeout)
})
return Promise.race([p, wait])
}
Copy the code
So you can use it like this:
const req = timeoutWrapper(request)
req.then(res= > console.log(res)).catch(e= > console.log(e))
Copy the code
This approach is not flexible, however, as there are many reasons to terminate a promise, such as manually terminating it when the user clicks a button or other events occur. So you should write a wrapper function that provides abort and let the consumer decide when to terminate:
function abortWrapper(p1) {
let abort
let p2 = new Promise((resolve, reject) = > (abort = reject))
let p = Promise.race([p1, p2])
p.abort = abort
return p
}
Copy the code
The use method is as follows:
const req = abortWrapper(request)
req.then(res= > console.log(res)).catch(e= > console.log(e))
setTimeout((a)= > req.abort('User manually terminates request'), 2000) // This can be the user's active click
Copy the code
Finally, again, although the promise is interrupted, the promise is not terminated and the network request may still return, but by then we don’t care about the result of the request.