聊聊前端并行和串行请求

4,607 阅读4分钟

这篇来说说请求的一些小细节、小知识点

现在的请求处理总是离不开Promiseasync/await

串行和并行也就是围绕着这两个玩意去写的

一、先说说并行请求

什么并行请求?

简单来说就是:请求1和请求2同时进行请求

如下图,在控制台中mock1和mock2的请求就是同时进行

646D49CC-E4E9-40B0-9336-04C80C2F65EC.png

提示:看waterfall更佳直观

那如何实现并行请求?

当然需要分情况

对于调用多个后台接口,且都没有直接相关的

只需要用两个函数进行封装,依次调用

例如使用Promise

// 请求1
function requestOne() {
    axios.get('http://localhost:5000/mock1')
        .then((res) => {
            console.log(res.data)
            // 请求后做些什么...
        })
}

// 请求2
function requestTwo() {
    axios.get('http://localhost:5000/mock2')
        .then((res) => {
            console.log(res.data)
            // 请求后做些什么...
        })
}

function requestTotal() {
    // 依次调用
    requestOne()
    requestTwo()
}

requestTotal()

async/await也一样,分别用两个函数进行分装

// 请求1
async function requestOne () {
    const res = await axios.get('http://localhost:5000/mock1')
    console.log(res.data)
    // 请求后做些什么...
}

// 请求2
async function requestTwo () {
    const res = await axios.get('http://localhost:5000/mock2')
    console.log(res.data)
    // 请求后做些什么...
}

function requestTotal() {
    // 依次调用
    requestOne()
    requestTwo()
}

requestTotal()

对于调用多个后台接口,且都有直接相关的

例如,我需要对所有的请求结果进行处理

那就是使用Promise.all

image.png

对于使用 promise ,那我们代码可以写成这样

// 请求1
function requestOne() {
    //  请求成功后返回 Promise 对象
    return new Promise((resolve) => {
        axios.get('http://localhost:5000/mock1')
            .then((res) => {
                resolve(res.data)
            })
    })
}

// 请求2
function requestTwo() {
    // 请求成功后返回 Promise 对象
    return new Promise((resolve) => {
        axios.get('http://localhost:5000/mock2')
            .then((res) => {
                resolve(res.data)
            })
    })
}

// 处理所有请求
function requestTotal() {
    console.log('requestOne()', requestOne())
    console.log('requestTwo()', requestTwo())
    // 处理请求1和请求2返回的 Promise 对象
    Promise.all([requestOne(), requestTwo()])
        .then((res) => {
            console.log(res)
            // 请求所有后做些什么...
        })
}

// 调用
requestTotal()

输出如下

image.png

async/await可以改成这样

// 请求1
async function requestOne () {
    const res = await axios.get('http://localhost:5000/mock1')
    return res.data
}

// 请求2
async function requestTwo () {
    const res = await axios.get('http://localhost:5000/mock2')
    return res.data
}

// 处理所有请求
function requestTotal() {
    // 处理请求1和请求2返回的 Promise 对象
    Promise.all([requestOne(), requestTwo()])
        .then((res) => {
            console.log(res)
            // 请求所有后做些什么...
        })
}

// 调用
requestTotal()

由于async函数会自动将结果用Promise.resolve包裹,直接返回即可

二、再说说串行请求

什么串行请求?

简单来说就是:请求1 -> 请求2 -> 请求3,这样依次执行

一个简单的场景需求就是:你必须拿请求1返回的数据后,作为请求2参数再去请求

例如:

// 请求1
function requestOne() {
    return new Promise((resolve) => {
        axios.get('http://localhost:5000/mock1')
            .then((res) => {
                resolve(res.data)
            })
    })
}

// 请求2
function requestTwo() {
    return new Promise((resolve) => {
        axios.get('http://localhost:5000/mock2')
            .then((res) => {
                resolve(res.data)
            })
    })
}

// 使用 promise 依次调用
function requestTotal() {
    requestOne().then(() => {
        requestTwo().then(() => {
        })
    })
}

// 调用
requestTotal()

如果你觉得requestTotal这个函数写法不优雅,完全可以这样写

// 使用 promise 依次调用
function requestTotal() {
   let chain = Promise.resolve()
    chain = chain.then(() => requestOne())
    chain = chain.then(() => requestTwo())
}

或者这样

function requestTotal() {
    Promise.resolve()
        .then(() => {
            return requestOne()
        })
        .then(() => {
            return requestTwo()
        })
}

如果觉得上面也太麻烦了,那就直接上async/await

// 使用 promise 依次调用
async function requestTotal() {
   await requestOne()
   await requestTwo()
}

👇全部简化代码:

// 请求1
async function requestOne() {
    const res = await axios.get('http://localhost:5000/mock1')
    return res.data
}
// 请求2
async function requestTwo() {
    const res = await axios.get('http://localhost:5000/mock2')
    return res.data
}
// 使用 promise 依次调用
async function requestTotal() {
    await requestOne()
    await requestTwo()
}
// 调用
requestTotal()

这里需要特别提醒小伙伴们使用async/await

有时不要觉得使用async/await就是串行了

例如

// 请求1
function requestOne() {
    axios.get('http://localhost:5000/mock1')
        .then((res) => {
            resolve(res.data)
        })
}
// 请求2
function requestTwo() {
    axios.get('http://localhost:5000/mock2')
        .then((res) => {
            // console.log(res.data)
            resolve(res.data)
        })
}

async function requestTotal() {
    await requestOne()
    await requestTwo()
}

requestTotal()

这其实个是并行请求!

你需要在调用函数中返回(return)出 Promise 对象

三、不要忽略异常处理

上面的所有代码都没有加入异常捕获,这显然是不好的

异常处理是可以说是项目健壮性的保障,所有请小伙伴们一定要重视

说到异常,很多小伙伴肯定会想到try/catchwindow.onerror

但,对于 Promise对象来说,他们并不能捕获其中的异常

你应该使用.catchwindow.onunhandledrejection

但,我不推荐window.onunhandledrejection这种全局异常

而更推荐.catch

例如

function requestTotal() {
    Promise.resolve()
        .then(() => {
            return  requestOne()
        })
        .catch(e => {
            // 捕获异常
            console.log(e)
        })
        .then(() => {
            return  requestTwo()
        })
        .catch(e => {
           // 捕获异常
           console.log(e)
        })
}

这里有个细节,为什么需要两个 .catch

如你没有对 requestOne 进行捕获,那么会阻塞requestTwo 运行

例如

function requestTotal() {
    Promise.resolve()
        .then(() => {
            return  requestOne()
        })
        .then(() => {
            // requestOne 发生异常,requestTwo 无法执行
            return  requestTwo()
        })
        .catch(e => {
           // 捕获异常
           console.log(e)
        })
}

如果小伙伴们决定麻烦,可以使用async/await

async function requestTotal() {
    try {
        await requestOne()
        await requestTwo()
    } catch (e) {
        console.log(e)
    }
}

因为async/await可以被try/catch捕获到

但这样 requestTwo 就无法继续执行

当然具体实现情况应该根据需求进行调整,这里只是强调细节

感谢阅读