如何做到所有请求完成loading消失?

RT.
Vue项目,封装了一层axios,只要调用axios就出现loading,然后在axios响应拦截器处让loading消失,这就导致了第一个请求完成后loading消失,后面的请求pending半天,用户总是感觉没数据,觉得现在唯一的办法是得知最后一个请求的状态

阅读 17.8k
7 个回答

非常感谢大家的解答,现在已经解决,自答一下,如果有更好的方法希望大家分享一下,谢谢

  var num = 0
  axios.interceptors.request.use(function (config) {  //在请求发出之前进行一些操作
    num++
    _bus.$emit('showloading')
    return config
  }

  axios.interceptors.response.use(response => {        // 接受请求后num--,判断请求所有请求是否完成
    num--
    if (num <= 0) {
      _bus.$emit('closeLoading')      
    } else {
      _bus.$emit('showloading')      
    }
  })
// 伪代码

showLoading();
const loads = urls.map(url => ajax(url));
await Promise.all(loads);
hideLoading();

2023-02-22 更新

今天又看到这个问题,补充个合并 loading 的示例

const { mockRemote, wrapLoading } = (() => {
    let loading = false;
    function mockRemote(tag, ms) {
        return new Promise(resolve => setTimeout(
            () => {
                console.log(`${tag} ${ms} ${Date.now() % 100000}`);
                resolve(ms);
            },
            ms)
        );
    }

    async function wrapLoading(fn) {
        if (loading) {
            return await fn();
        }

        loading = true;
        const mark = Date.now();
        console.log(`[${mark}] loading`);
        try {
            return await fn();
        } finally {
            console.log(`[${mark}] loaded`);
        }
    }

    return { mockRemote, wrapLoading };
})();

const b1 = async () => await mockRemote("b1", 1000);
const b2 = async () => await wrapLoading(() => mockRemote("b2", 500));
const b3 = async () => await mockRemote("b3", 800);

console.log("----------------------------");
console.log("testing single loading");
console.log("----------------------------");
await b2();

console.log("----------------------------");
console.log("testing merge lading");
console.log("----------------------------");
await wrapLoading(async () => {
    await b1();
    await b2();
    await b3();
});

比较好的方式是全局统一拦截器,js是单线程的,所以使用一个变量记录下当前进行中的请求

let requestingCount = 0;

const handleRequestLoading = () => {
  if (!requestingCount)  startLoading()
  requestingCount++
}
const handleResponseLoading = () => {
  requestingCount--
  if (!requestingCount)  stopLoading()
}

axiosInstance.interceptors.request.use(config => {
    handleRequestLoading()
  },() => {
    handleResponseLoading()
  })

axiosInstance.interceptors.response.use(response => {
    handleResponseLoading()
  }, error => {
    handleResponseLoading()
  })

方案1. 七个请求做成队列,第一个完成再请求第二个。以此类推直到最后一个结束
方案2. 七个请求任意一个完成之后检查当前完成数(可以是简单的计数),如果满七干嘛干嘛,否则啥都不干

现在流行的 Promise 也可以,不过暂时不会用

let p1 = new Promise((resolve, reject) => {
    resolve(1);
})
let p2 = new Promise((resolve, reject) => {
    resolve(2);
})
let p3 = new Promise((resolve, reject) => {
    resolve(3);
})
Promise.all([p1, p2, p3]).then(values => { 
    console.log(values); 
});
新手上路,请多包涵

如果有些请求需要显示loading,有些不需要显示,这种方式可能就不太适用。。。

官方例子:
function getUserAccount() {
return axios.get('/user/12345');
}

function getUserPermissions() {
return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {

// Both requests are now complete

}));

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏