Javascript Axios/Vue-防止Axios.all()继续执行

Javascript Axios/Vue-防止Axios.all()继续执行,javascript,vue.js,axios,vue-cli,Javascript,Vue.js,Axios,Vue Cli,在我的应用程序中,我调用fetchData函数对用户进行身份验证。如果用户令牌无效,应用程序将运行axios.all(),我的拦截器将返回大量错误 如何防止axios.all()在第一个错误后继续运行?并且只向用户显示一个通知 拦截器.js export default (http, store, router) => { http.interceptors.response.use(response => response, (error) => {

在我的应用程序中,我调用fetchData函数对用户进行身份验证。如果用户令牌无效,应用程序将运行
axios.all()
,我的拦截器将返回大量错误

如何防止axios.all()在第一个错误后继续运行?并且只向用户显示一个通知

拦截器.js

export default (http, store, router) => {
    http.interceptors.response.use(response => response, (error) => {
        const {response} = error;

        let message = 'Ops. Algo de errado aconteceu...';

        if([401].indexOf(response.status) > -1){
            localforage.removeItem('token');

            router.push({
                name: 'login'
            });

            Vue.notify({
                group: 'panel',
                type: 'error',
                duration: 5000,
                text: response.data.message ? response.data.message : message
            });
        }

        return Promise.reject(error);
    })
}
auth.js

const actions = {
    fetchData({commit, dispatch}) {
        function getChannels() {
            return http.get('channels')
        }

        function getContacts() {
            return http.get('conversations')
        }

        function getEventActions() {
            return http.get('events/actions')
        }

        // 20 more functions calls

        axios.all([
            getChannels(),
            getContacts(),
            getEventActions()
        ]).then(axios.spread(function (channels, contacts, eventActions) {
            dispatch('channels/setChannels', channels.data, {root: true})
            dispatch('contacts/setContacts', contacts.data, {root: true})
            dispatch('events/setActions', eventActions.data, {root: true})
        }))
    }
}
编辑:更好,因为它允许取消第一次出错后仍挂起的请求,并且不需要任何额外的库。


一种解决方案是为您同时使用的所有请求分配一个唯一标识符(在本例中,我将使用
uuid/v4
包,请随意使用其他内容):

import uuid from 'uuid/v4'

const actions = {
    fetchData({commit, dispatch}) {
        const config = {
            _uuid: uuid()
        }

        function getChannels() {
            return http.get('channels', config)
        }

        function getContacts() {
            return http.get('conversations', config)
        }

        function getEventActions() {
            return http.get('events/actions', config)
        }

        // 20 more functions calls

        axios.all([
            getChannels(),
            getContacts(),
            getEventActions()
        ]).then(axios.spread(function (channels, contacts, eventActions) {
            dispatch('channels/setChannels', channels.data, {root: true})
            dispatch('contacts/setContacts', contacts.data, {root: true})
            dispatch('events/setActions', eventActions.data, {root: true})
        }))
    }
}
然后,在拦截器中,您可以选择使用此唯一标识符一次性处理错误:

export default (http, store, router) => {
    // Here, you create a variable that memorize all the uuid that have
    // already been handled
    const handledErrors = {}
    http.interceptors.response.use(response => response, (error) => {
        // Here, you check if you have already handled the error
        if (error.config._uuid && handledErrors[error.config._uuid]) {
            return Promise.reject(error)
        }

        // If the request contains a uuid, you tell 
        // the handledErrors variable that you handled
        // this particular uuid
        if (error.config._uuid) {
            handledErrors[error.config._uuid] = true
        }

        // And then you continue on your normal behavior

        const {response} = error;

        let message = 'Ops. Algo de errado aconteceu...';

        if([401].indexOf(response.status) > -1){
            localforage.removeItem('token');

            router.push({
                name: 'login'
            });

            Vue.notify({
                group: 'panel',
                type: 'error',
                duration: 5000,
                text: response.data.message ? response.data.message : message
            });
        }

        return Promise.reject(error);
    })
}

另外,您可以将
fetchData
函数简化为:

建议的解决方案需要等待所有响应完成,依赖于
uuid
,并在拦截器中增加一些复杂性。我的解决方案避免了所有这些,并实现了终止
Promise.all()
执行的目标

Axios支持,因此您可以使用错误处理程序包装
GET
请求,该错误处理程序可立即取消其他挂起的请求:

fetchData({ dispatch }) {
  const source = axios.CancelToken.source();

  // wrapper for GET requests
  function get(url) {
    return axios.get(url, {
        cancelToken: source.token // watch token for cancellation
      }).catch(error => {
        if (axios.isCancel(error)) {
          console.warn(`canceled ${url}, error: ${error.message}`)
        } else {
          source.cancel(error.message) // mark cancellation for all token watchers
        }
      })
  }

  function getChannels() {
    return get('https://reqres.in/api/users?page=1&delay=30'); // delayed 30 secs
  }
  function getContacts() {
    return get('https://reqres.in/api/users?page=2'); // no delay
  }
  function getEventActions() {
    return get('https://httpbin.org/status/401'); // 401 - auth error
  }

  ...
}
在拦截器中,您还可以忽略来自请求取消的错误:

export default (http, store, router) => {
  http.interceptors.response.use(
    response => response,
    error => {
      if (http.isCancel(error)) {
        return Promise.reject(error)
      }

      ...

      // show notification here
    }
}

作为Axios cancel的替代方案,您可以使用更简单的方法

与旧取消相比,新取消的优点是:

  • .cancel()是同步的
  • 取消操作无需设置代码
  • 与其他蓝鸟功能组合,如Promise.all
这是一个演示。我添加了一些登录axios.get(…)。然后(…)来跟踪每个调用是否完成

注释掉行
promises.forEach(p=>p.cancel())
,以验证在不取消的情况下,无错误调用将一直运行到完成

//对于演示,请检查提取是否完成
const logCompleted=(res)=>console.log(`Promise completed',${res.config.url}`)
函数getChannels(){
返回axios.get(“https://reqres.in/api/users?page=1&delay=5)然后(完成日志)
}
函数getContacts(){
返回axios.get(“https://reqres.in/api/users?page=2)然后(完成日志)
}
函数getEventActions(){
返回axios.get(“https://httpbin.org/status/401)然后(完成日志)
}
Promise.config({cancellation:true});//蓝鸟形态
window.Promise=Promise;//axios承诺现在是蓝鸟口味
常量承诺=[GetChannel()、getContacts()、getEventActions()];
所有(承诺)
。然后(([频道、联系人、事件操作])=>{
log('Promise.all.then',{channels,contacts,eventActions});
})
.catch(错误=>{
log(`Promise.all.catch,${err.message}`)
promises.forEach(p=>p.cancel());
})
.finally(()=>console.log('Promise.all.finally'))


请参见。您也可以用
异步生成器
异步迭代器
替换
;另请参见。
axio.all()
使用
Promise.all()
。您能否演示在第一次异常或被拒绝后继续执行的
Promise.all()
?为什么
.catch()
没有链接到
.then()
来处理错误?
axios.all
不执行任何操作,也不能“停止”任何操作。它只是建立了一个等待其他承诺的承诺。您正在立即调用
getChannels()
getContacts()
getEventActions()
,当您从它们那里得到第一个错误时,它们都已经在运行了。您最好的办法可能是发出一个请求,检查用户令牌是否有效,并且只有在成功后才运行其他令牌。不是依靠拦截器。嗨,我看到你在这个问题上重新开始了悬赏。我的回答怎么没有回答你的问题?你还有其他我的答案不能满足的要求吗?这是我最喜欢的一个,因为它只使用Axios功能,而且正如你在这里所说的,投票结果是等待所有承诺完成。然而,我无法理解如何调用拦截器中第一次失败的通知。是吗?@Hammerbot当取消令牌被调用时,它会对该令牌上所有其他挂起的请求抛出一个错误,这会导致侦听器的错误处理程序被调用。好的,我明白了。该通知之所以显示,是因为第一个错误在第一次取消之前通过了拦截器。这肯定是经过验证的答案。我正在编辑我的答案以提及这一点。
export default (http, store, router) => {
  http.interceptors.response.use(
    response => response,
    error => {
      if (http.isCancel(error)) {
        return Promise.reject(error)
      }

      ...

      // show notification here
    }
}