# 终止异步任务
在一些耗时的异步任务中,我们需要提前终止异步任务,使用AbortController (opens new window)构造函数我们可以做到这一点。
需要注意的是AbortController只能在web请求中使用,不支持在nodejs环境中使用。
# 取消fetch请求
目前只有Fetch Api官方支持使用AbortController来终止请求,但是并不影响我们使用它来终止其他的异步任务,后面会讲到, 先来看一下AbortController的基本使用方法。
var controller = new AbortController(); // 1. 声明AbortController实例
var signal = controller.signal; // 2. 将signal传入5中的fetch api,
var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');
downloadBtn.addEventListener('click', fetchVideo); // 3. 点击按钮开始下载音频
abortBtn.addEventListener('click', function() { // 4. 点击中断按钮终止下载
controller.abort();// 5. 调用abort方法触发终止
console.log('Download aborted');
});
function fetchVideo() {
...
fetch(url, {
signal // 6. 传入signal参数
}).then(function(response) {
...
}).catch(function(e) {
// 7. 接收终止异常
reports.textContent = 'Download error: ' + e.message;
})
}
这里需要解释一下controller.signal属性,这里的signal属性是AbortSignal (opens new window)构造函数的实例, 它具有以下特性:
- aborted属性:aborted是一个布尔值,用来标记是否调用controller.abort()方法终止请求,是为true,否为false
- 使用addEventListener()方法监听abort事件,调用controller.abort()后会触发此事件。
# 终止异步函数
有了上面的基础,我们完全可以实现终止异步函数的功能,这里我们实现一个下载按钮,点击下载按钮开始下载, 这里使用setTimeout来模拟下载需要10s,在这10s内用户可以再次点击按钮取消下载,下面是简单的实现代码。
<html>
<body>
<button id="download">Download</button>
</body>
<script>
{
let controller = null
let isDownloading = false
document.querySelector( '#download' ).addEventListener( 'click',async ({target})=>{
if(isDownloading){ // 如果正在下载中再次点击按钮则取消下载并重置变量
controller.abort();
controller = null;
target.innerText = 'Download';
isDownloading = false
return;
}else {
controller = new AbortController()
isDownloading = true
const signal = controller.signal
target.innerText = 'Downloading';
try {
const result = await download(signal)
console.log("===下载成功===",)
} catch(error) { // 下载取消后会触发AbortError这里可以catch到
console.log("===下载已取消===",error)
} finally { // 最后无论下载成功还是下载取消都需要对变量进行重置
controller = null;
isDownloading = false
target.innerText = 'Download';
}
}
})
function download(signal) {
const error = new DOMException( 'Download aborted', 'AbortError' );
if(signal.aborted){ // 如果有提前终止的情况直接退出
return reject( error );
}
return new Promise((resolve,reject)=>{
const timeout = setTimeout( ()=> {
resolve();
clearTimeout(timeout)
}, 10000 );
signal.addEventListener( 'abort', () => { // 接收到abort事件提前结束
clearTimeout( timeout );
reject( error );
} );
})
}
}
</script>
</html>
# 参考资料
← 被几个月代理leader CI/CD 概念 →