代数效应-消除异步传染性

68 阅读1分钟

先看下面的代码,由于userInfo是异步的,导致后面调用userInfo的方法以及相关联方法,全都需要使用async,await处理。这就是异步的传染性

async function userInfo() {
    // 接口请求
    return await (await fetch('./index.json')).json()
}

async function getUser() {
    let user = await userInfo()
    return user
}

async function m1() {
    let user = await getUser()
    return user
}

async function main() {
    let user = await getUser()
    return user
}

main().then(ret => {
    console.log(ret)
})

问题

怎么可以消除异步传染呢,在后续函数调用的时候去掉async await

function userInfo() {
    return fetch('./index.json')
}

function getUser() {
    let user = userInfo()
    return user
}

function m1() {
    let user = getUser()
    return user
}

function main() {
    let user = m1()
    console.log(user)
}

解决思路

为什么会出现这种情况?因为请求一定会是异步的,为了从根源解决问题,我们改造fetch方法,把fetch方法改成同步的就好了

fetch改造

fetch函数内部使用缓存存储请求结果,当执行fetch函数时:

  • 如果请求有缓存就直接使用缓存
  • 如果没有缓存
    • 直接抛出异常。
    • 当前函数继续执行请求,并缓存请求结果

最后,在函数入口处执行函数并捕获异常,如果有异常就在一定时机重新执行函数

// 这里就只考虑一个请求的情况
let oriFetch = window.fetch
let cache = {
    status: 'pending',
    value: null
}
window.fetch = function(...args) {
    if (cache.status === 'fullfilled') {
        return cache.value
    }
    if (cache.status === 'reject') {
        throw cache.value
    }

    let prom = oriFetch(...args).then(ret=> ret.json()).then(ret => {
        cache.status = 'fullfilled'
        cache.value = ret
    }).catch(err => {
        cache.status = 'reject'
        cache.value = err
    })

    throw prom
}

function run(func) {
    try {
        func()
    } catch(err) {
        if (err instanceof Promise) {
            err.then(func, func).finally(() => {
                window.fetch = oriFetch
                cache.status = 'pending'
                cache.value = null
            })
        }
        
    }
}

run(main)