axios

  • axios拦截器
  • axios取消发送请求
  • 基本流程
  • 实例演示


axios拦截器


以我的理解,拦截器就是一个回调函数,里面包含了一些逻辑处理的代码,它分为请求拦截器,其在请求发送之前执行,处理一些请求发送之前的逻辑,响应拦截器,其在响应拿到之后,回调函数执行之前执行,处理一些拿到响应数据的逻辑。

为了方便演示,我使用koa写了一个简易的接口,用于处理请求返回数据

const Koa = require('koa');
const router = require('koa-router')();
const jsonp = require('koa-jsonp');
const cors = require('koa-cors');

var app = new Koa();

app.use(cors());
app.use(jsonp());

async function delay(time) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve()
        }, time)
    })
}

router.get('/products1', async (ctx) => {
    await delay(1000 + Math.random() * 1000)
    ctx.body = [{
            id: 1,
            name: 'products1.1'
        },
        {
            id: 2,
            name: 'products1.2'
        },
        {
            id: 3,
            name: 'products1.3'
        }
    ];
});

router.get('/products2', async (ctx) => {
    await delay(1000 + Math.random() * 1000)
    ctx.body = [{
            id: 1,
            name: 'products2.1'
        },
        {
            id: 2,
            name: 'products2.2'
        },
        {
            id: 3,
            name: 'products2.3'
        }
    ];
});

app.use(router.routes());
app.use(router.allowedMethods());
app.listen(4000);

console.log('Server running at http://localhost:4000');

演示

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
    <script>
        //添加请求拦截器(回调函数)
        axios.interceptors.request.use(
            config => {
                console.log('request interceptor1 onResolved()')
                return config
            },
            error => {
                console.log('request interceptor1 onRejected()')
                return Promise.reject(error)
            }
        )
        axios.interceptors.request.use(
            config => {
                console.log('request interceptor2 onResolved()')
                return config
            },
            error => {
                console.log('request interceptor2 onRejected()')
                return Promise.reject(error)
            }
        )
        //添加响应拦截器
        axios.interceptors.response.use(
            response => {
                console.log('response interceptor1 onResolved()')
                return response
            },
            error => {
                console.log('response interceptor1 onRejected()')
                return Promise.reject(error)
            }
        )
        axios.interceptors.response.use(
            response => {
                console.log('response interceptor2 onResolved()')
                return response
            },
            error => {
                console.log('response interceptor2 onRejected()')
                return Promise.reject(error)
            }
        )

        axios.get('http://localhost:3000/posts')
            .then(response => {
                console.log('data', response.data);

            })
            .catch(error => {
                console.log('error', error.message)
            })
    </script>
</body>

</html>


请求拦截器2 ------请求拦截器1-------发送ajax请求-------响应拦截器1-------响应拦截器2------响应回调

此流程是通过Promise串联起来的,请求拦截器传递的是config,响应拦截器传递的是response


axios取消发送请求

取消发送请求,顾名思义就是取消正在发送的ajax请求,这在业务场景中是会遇到的

基本流程
  1. 配置cancleToken对象
  2. 缓存用于取消的cancel函数
  3. 在后面特定时机调用cancel取消请求
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>取消请求</title>
</head>

<body>
    <div>
        <button onclick="getOne()">获取商品列表1</button>
        <button onclick="getTwo()">获取商品列表2</button>
        <button onclick="cancelReq()">取消请求</button>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
    <script>
        let cancel //用于保存取消请求的函数
        function getOne() {
            axios({
                    url: 'http://localhost:4000/products1',
                    cancelToken: new axios.CancelToken((c) => { //c是用于取消当前请求的函数
                        //保存取消函数,用于之后可能需要取消当前请求
                        cancel = c
                    })
                })
                .then(
                    //不管成功还是失败,都应该没有机会取消了
                    response => {
                        cancel = null
                        console.log('请求1成功了', response.data)
                    },
                    error => {
                        cancel = null
                        console.log('请求1失败了', error.message)
                    }
                )
        }

        function getTwo() {
            axios({
                    url: 'http://localhost:4000/products2',
                })
                .then(
                    response => {
                        console.log('请求2成功了', response.data)
                    },
                    error => {
                        console.log('请求2失败了', error.message)
                    }
                )
        }

        function cancelReq() {
            //alert('取消请求')
            //执行取消请求的函数
            if (typeof cancel === 'function') {
                cancel('强制取消请求')
            } else {
                console.log('没有可取消的请求')
            }
        }
    </script>
</body>

</html>

实例演示

上面的那个例子实现了点击按钮取消发送请求,但是我们会遇到一个情况,用户点击获取列表1的数据之后又点击获取列表2的数据,若列表1和列表2与前台同一位置显示,那么先前请求的列表1的数据的请求是否就可以直接中断而光去请求列表2的数据,这就需要实现在请求一个接口前,取消前面一个未完成的请求

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>取消请求</title>
</head>

<body>
    <div>
        <button onclick="getOne()">获取商品列表1</button>
        <button onclick="getTwo()">获取商品列表2</button>
        <button onclick="cancelReq()">取消请求</button>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
    <script>
        let cancel

        function getOne() {
            if (typeof cancel === 'function') {
                cancel('强制取消')
            }
            axios({
                url: 'http://localhost:4000/products1',
                cancelToken: new axios.CancelToken(c => {
                    cancel = c
                })
            }).then(
                response => {
                    cancel = null
                    console.log("请求1成功了");
                },
                error => {
                    if (axios.isCancel(error)) {
                        console.log("取消1导致的错误,不应将cancel置空");
                    } else {
                        cancel = null
                        console.log("请求1失败了");
                    }
                }
            )
        }

        function getTwo() {
            if (typeof cancel === 'function') {
                cancel('强制取消')
            }
            axios({
                url: 'http://localhost:4000/products2',
                cancelToken: new axios.CancelToken(c => {
                    cancel = c
                })
            }).then(
                response => {
                    cancel = null
                    console.log("请求2成功了");
                },
                error => {
                    if (axios.isCancel(error)) {
                        console.log("取消2导致的错误,不应将cancel置空");
                    } else {
                        cancel = null
                        console.log("请求2失败了");
                    }
                }
            )
        }

        function cancelReq() {
            if (cancel) {
                cancel('强制取消')
            } else {
                console.log("没有可以取消的请求");
            }
        }
    </script>
</body>

</html>

因为所有请求都是用的一个cancel,所以cancel为function,则证明前一个请求并没有完成,因为完成请求之后cancel会被置为null

注意这里要写成上面这样,而不是下面

error => {
                    if (axios.isCancel(error)) {
                        console.log("取消1导致的错误,不应将cancel置空");
                    } else {
                        cancel = null
                        console.log("请求1失败了");
                    }
                }
error => {
cancel = null
console.log("请求1失败了");
}

因为error是异步执行的
比如:点击发送第一次 ---- 点击发送第二次,这时第一次的请求被中断,但是因为error是异步的,所以error的代码并没有马上执行-----点击发送第三次,这是error的代码执行了,cancel被置为null,但此时第二次请求仍在请求中,进入第三次的判断中,cancle不为function,所以无法中断第二次发送的请求,我们使用axios中的isCancel函数可以判断错误是否是因为取消发送请求产生的,若是因为取消发送请求产生的,我们不将cancel置为空就可以解决这个问题

这样虽然完成了要求,但是我们发现重复的地方很多,若是请求多了之后,难道每一个都要再写一遍?有人会想到封装成函数,这里封装成函数并没有改变多少,还是每一个都要调用
这就要使用到我上面提到的拦截器了,拦截器可以对所用的请求(响应)做处理,既然所有的请求(响应)里面都有这样相似的代码,用拦截器在请求发送之前(响应得到之后)一起处理,岂不美哉,这就是拦截器的好处,下面我们来看

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>取消请求</title>
</head>

<body>
    <div>
        <button onclick="getOne()">获取商品列表1</button>
        <button onclick="getTwo()">获取商品列表2</button>
        <button onclick="cancelReq()">取消请求</button>
    </div>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
    <script>
        //添加请求拦截器
        axios.interceptors.request.use((config) => {
            //1.在准备发请求前,取消上一个未完成的请求
            if (typeof cancel === 'function') {
                cancel('强制取消')
            }
            //2.添加一个cancelToken的配置
            config.cancelToken = new axios.CancelToken(c => {
                cancel = c
            })
            return config
        })

        //添加一个响应拦截器
        axios.interceptors.response.use(
            (response) => {
                cancel = null
                return response
            },
            error => {
                if (axios.isCancel(error)) {
                    console.log("取消导致的错误,不应将cancel置空");
                    //中断Promise链
                    return new Promise(() => {})
                } else {
                    cancel = null
                    //非取消导致的错误,将错误向下传递 让请求自己处理
                    return Promise.reject(error)
                    //也可以抛出错误 throw error
                }
            }
        )

        let cancel

        function getOne() {
            axios({
                url: 'http://localhost:4000/products1'
            }).then(
                response => {
                    console.log("请求1成功了");
                },
                error => {
                    //只用处理请求失败的情况,取消请求导致的错误不用处理
                    console.log("请求1失败了");
                }
            )
        }

        function getTwo() {
            axios({
                url: 'http://localhost:4000/products2'
            }).then(
                response => {
                    console.log("请求2成功了");
                },
                error => {
                    console.log("请求2失败了");
                }
            )
        }

        function cancelReq() {
            if (cancel) {
                cancel('强制取消')
            } else {
                console.log("没有可以取消的请求");
            }
        }
    </script>
</body>

</html>