首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >常用!中断 promise 的 2 种场景,收藏等于学会

常用!中断 promise 的 2 种场景,收藏等于学会

作者头像
掘金安东尼
发布2023-01-10 14:02:05
1.3K0
发布2023-01-10 14:02:05
举报
文章被收录于专栏:掘金安东尼掘金安东尼

想一想,Promise 如何实现中断?

讲道理,我们回忆下就知道 Promise 的特性就是:不能中断。

一旦执行,我们无法知道它具体执行到哪里了,只知道在 pending,最后 resolve 或者 reject 才知道执行完毕。

但需要中断的这种应用场景也确实是存在的。

比如:

1. 点击按钮,发起请求,再点击另外一个按钮,通过中断 Promise,取消请求;

2. 用 Promise 封装异步请求,当超过 N 秒后还未执行完,中断 Promise ,执行后续操作;

中断,相当于手动执行 reject 的后续内容;

  • 这里的取消请求,并不是撤回 XHR 请求,而是不再需要请求结果,直接执行后面的步骤;

p1

如何实现?

不急,先想想,同步的中断 promise 的情况,它大概是这样的:

function someAsyncFunction() {
  return new Promise(function(resolve, reject) {
    // 在这里执行异步操作
    if (/* 某个条件成立 */) {
      // 如果条件成立,中断 promise 
      reject(new Error("The promise was interrupted"));
    }
  });
}

someAsyncFunction().catch(function(error) {
  // 处理 promise 中断的回调函数
  console.error(error.message);
});

没什么毛病,如果某个条件成立,reject 错误信息;

那么,那对于第一个问题,就很好理解了:

  1. 点击按钮,发起请求,再点击另外一个按钮,通过中断 Promise,取消请求;

实现步骤拆解:

  1. 为了方便测试,我们找一个可供在线测试的 API https://jsonplaceholder.typicode.com/posts GET 请求可以直接拿到返回报文;
  2. 不借助请求库,就用原生 XHR;
  3. 为了加强模拟效果,我们再用一个 setTimeout 函数,延长成功返回的时间,意思是:请求至少要 10s+ 才会成功返回;
  4. 写一个全局的 cancelFn 方法,然后在 promise 内部重写它,当调用时,会直接 reject ,便实现了中断;
const baseURL='https://jsonplaceholder.typicode.com/posts';

let cancelFn=function(){}

function request(req){
    return new Promise((resolve,reject)=>{
        let xhr=new XMLHttpRequest();
        xhr.open(req.method || 'GET',baseURL);
        xhr.onload=function(){
            if(xhr.readyState==4 &amp;&amp; (xhr.status>=200 &amp;&amp; xhr.status<300)){
                setTimeout(()=>{
                    resolve({data:JSON.parse(xhr.responseText)})
                },10000)
            }else{
                reject(xhr.status)
            }
        }
        xhr.onerror=function(){
            reject('中断 promise...')
        }
        xhr.send(req.data || null);

        cancelFn=function(msg){
            reject({message:msg})
        }
    })
};

let send=document.querySelector('.send');
let cancel=document.querySelector('.cancel');
send.addEventListener('click',async function(){
    console.log('正在请求中...')
    let {data}=await request({})
    console.log(data)
});
cancel.addEventListener('click',function(){
    cancelFn('中断 promise');
})

可以在码上掘金,打开控制台测测看。

https://code.juejin.cn/pen/7173900335335866407

p2

对于第 2 个问题:

  1. 用 Promise 封装异步请求,当超过 N 秒后还未执行完,中断 Promise ,执行后续操作;

解决这个问题,用到一个巧思:

Promise.race:

一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝。

我们把手动执行的超时中断,和业务逻辑的 prosmie 链条放在一起,超过 N 秒后,调用 cancelFn 方法,在 race 的 竞争策略 下,若 N 秒后请求还没返回,则直接 reject 返回,则实现了中断;

代码实现:

const baseURL='https://jsonplaceholder.typicode.com/posts';

let cancelFn=function(){}

let readUrlPromise=url=>{
    return new Promise((resolve,reject)=>{
        let xhr=new XMLHttpRequest();
        xhr.open("GET",url);
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4 &amp;&amp; xhr.status==200){
                setTimeout(()=>{
                        resolve({data:JSON.parse(xhr.responseText)})
                },3000) // 用 setTimeout 假设请求至少需要 3 s
            }else if(xhr.readyState==4 &amp;&amp; xhr.status!=200){
                reject('请求失败');
            }
        }
        xhr.onerror=function(){
            reject('请求失败');
        }
        xhr.send(null);
        cancelFn=function(msg){
            reject(msg);
        }
    })
}

let rest=function(N){
    return Promise.race([
        readUrlPromise(baseURL),
        uploadTimeout(N)
    ]).then(data=>{
        console.log('url1');
        console.log(data);
    })
}

function uploadTimeout(N){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            cancelFn('请求超时,中断promise')
        },N*1000)
    })
}

rest(2) // 设定 2 s 后中断 promise;

控制台截图:

image.png
image.png

如果 N < 请求响应时间,则不会触发中断拦截;

https://code.juejin.cn/pen/7174026521235963912

另外,要提一下的是,著名请求库 axios。也有中断请求的功能,同样是利用 promise 实现一个竞态限制,有兴趣可自行研究;


OK,以上便是本篇分享,希望各位工友喜欢~ 欢迎点赞、收藏、评论 🤟 我是掘金安东尼 🤠 100 万人气前端技术博主 💥 INFP 写作人格坚持 1000 日更文 ✍ 关注我,安东尼陪你一起度过漫长编程岁月 🌏

😸 我的公众号:掘金安东尼,在上面,不止编程,更多还有生活感悟~

😺 我的 GithubPage: https://tuaran.github.io,它已经被维护 4 年+ 啦~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • p1
  • p2
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档