frontend-interview-question-and-answer icon indicating copy to clipboard operation
frontend-interview-question-and-answer copied to clipboard

[快手]如何对请求进行缓存

Open mqyqingfeng opened this issue 5 years ago • 3 comments

如何对请求进行缓存,例如有10个异步请求,如果有一个异步请求返回结果剩下的请求就用这个结果,并且能过传入成功和失败的回调函数

mqyqingfeng avatar Mar 09 '20 13:03 mqyqingfeng

使用强缓存

表示在缓存期间不需要请求

可以通过设置HTTP Header 实现:

  • Expires
  • Cache-Control

Expires

  • Expires是HTTP/1中的,表示资源在指定时间之后失效,需要重新请求
  • 受限制与本地时间,可以通过修改本地时间导致其失效
// 2020-03-10 11:11:28 后失效,需要重新请求
res.setHeader('Expires','Tue Mar 10 2020 11:11:28 GMT+0800')

Cache-Control

  • Cache-Control 出现于 HTTP/1.1,优先级高于 Expires
  • 可以组合使用多种指令
// 10s后失效
 res.setHeader('Cache-control', 'max-age=10')
常见指令
指令 作用
public 响应可以被服务端或者客户顿缓存
private 响应只可以被客户端缓存
max-age=30 缓存30s后过期需要重新请求
s-maxage=30 覆盖max-age,作用一致,代理服务器才生效
no-store 不缓存任何响应
no-cache 资源能被缓存,但立即失效
max-stale=30 30s内,即使缓存过期也使用该缓存
min-fresh=30 希望30s内获取最新的响应

使用示例(以Node为例)

const http = require('http')

let server = http.createServer(async (req, res) => {
    //  -------跨域支持-----------
    // 放行指定域名
    res.setHeader('Access-Control-Allow-Origin', '*')
    //跨域允许的header类型
    res.setHeader("Access-Control-Allow-Headers", "*");

    let { method, url } = req
    if (method === 'OPTIONS') {
        return res.end()
    }
    console.log(method, url)
    if (url === '/api/expires') {
        // 设置过期时间
        res.setHeader('Expires', 'Tue Mar 10 2020 11:11:28 GMT+0800')
        return res.end(`Expires---${new Date()}`)
    }
    if (url === '/api/cache') {
        // 设置10s后过期,需重新请求
        res.setHeader('Cache-control', 'max-age=10')
        return res.end(`Cache-control---${new Date()}`)
    }
    res.end('success')
})

// 启动
server.listen(3000, err => {
    console.log(`listen 3000 success`);
})

Axios请求示例

<button id="cache">cache</button>
    <button id="expires">expires</button>
    <script>
        expires.addEventListener('click', function () {
            for (let i = 0; i < 10; i++) {
                axios.get('http://localhost:3000/api/expires')
                    .then(res => {
                        let { status, data } = res
                        console.log(status, data)
                    })
            }
        })

        cache.addEventListener('click', function () {
            for (let i = 0; i < 10; i++) {
                axios.get('http://localhost:3000/api/cache')
                    .then(res => {
                        let { status, data } = res
                        console.log(status, data)
                    })
            }
        })
    </script>

结果 客户端请求 图片

图片 服务端响应一次 图片

图片

ATQQ avatar Mar 10 '20 03:03 ATQQ

简单写下针对GET 请求进行缓存的就可以了吧,其他思路都差不多

function request(url, success, failure) {
    const cache = request.cache[url];
    let promise = null;
    if (!cache) {
        console.log('not cache');
        promise = fetch(url);
        request.cache[url] = promise;
    } else {
        console.log('cache');
        promise = request.cache[url];
    }
    console.log(promise);
    promise
        .then(res => {
            return res.clone().json()
        })
        .then(json => {
            success(json);
        })
        .catch(error => {
            failure(error);
        })
}
request.cache = {};

recover758126 avatar Mar 21 '20 14:03 recover758126

const promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 500, 'one');
});

const promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 100, 'two');
});


function getRaceApis(promiseArr){
    return new Promise(function(resolve, reject) {
        try {
            Promise.race(promiseArr).then(function(value) {
                return Promise.allSettled(promiseArr).
                then((results) => results.forEach((result) => console.log(value)));
                // Both resolve, but promise2 is faster
            })
        } catch(err) {
            reject(err)
        }
    });
}


getRaceApis([promise1, promise2]).then((result) => {console.info(result)})

image

jokingzhang avatar Mar 21 '20 14:03 jokingzhang