Описывается следующим образом
- Отправляйте несколько одинаковых запросов одновременно. Если первый запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос.
- Если первый запрос не удался, то будет отправлен запрос с номером 2. Если запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос.
- Если второй запрос не удался, то отправьте запрос под номером 3. Если запрос будет успешным, остальные запросы не будут отправлены, а успешный результат будет возвращен как оставшийся запрос
-
...
Эта рекурсия, пока в худшем случае не потребуется отправить последний запрос
Параллелизм: запрос интерфейса все еще находится на рассмотрении, и тот же запрос отправляется через короткий промежуток времени.
async function fetchData (a) {
const data = await fetch('//127.0.0.1:3000/test')
const d = await data.json();
console.log(d);
return d;
}
fetchData(2) // 编号 1
fetchData(2) // 2
fetchData(2) // 3
fetchData(2) // 4
fetchData(2) // 4
fetchData(2) // 5
fetchData(2)
fetchData(2)
старая версияcachedAsync
я использовал раньшеvue
Функция cache кеширует успешные запросы, реализация такая. последующийcachedAsync
Только успешные запросы будут кэшироваться, и если они не пройдут, новый запрос будет получен напрямую. Но если это описанный выше параллельный сценарий, поскольку один и тот же запрос не может попасть в кеш, возникнет проблема с отправкой трех запросов подряд, и этот параллельный сценарий не может быть обработан.
const cachedAsync = function(fn) {
const cache = Object.create(null);
return async str => {
const hit = cache[str];
if (hit) {
return hit;
}
// 只缓存成功的Promise, 失败直接重新请求
return (cache[str] = await fn(str));
};
};
const fetch2 = cachedAsync(fetchData)
fetch2(2);
fetch2(2);
fetch2(2);
Расширенная версия
В первую очередь необходимо кэширование, затем нам нужно только разобраться, как контролировать параллелизм. Может быть такая идея
- Каждый запрос возвращает новый промис, а время выполнения эксектора промиса сохраняется в очереди.
- Когда длина очереди равна 1, выполнить запрос, если запрос успешен, затем пройти по эксектору в очереди, получить результат запроса и разрешить.
- Если запрос не выполнен, то этот Promise Reject отбрасывается, и стек помещается. Затем рекурсивно вызовите
next
- пока очередь exector не будет очищена
const cacheAsync = (promiseGenerator, symbol) => {
const cache = new Map();
const never = Symbol();
return async (params) => {
return new Promise((resolve, reject) => {
// 可以提供键值
symbol = symbol || params;
let cacheCfg = cache.get(symbol);
if (!cacheCfg) {
cacheCfg = {
hit: never,
exector: [{ resolve, reject }],
};
cache.set(symbol, cacheCfg);
} else {
// 命中缓存
if (cacheCfg.hit !== never) {
return resolve(cacheCfg.hit)
}
cacheCfg.exector.push({ resolve, reject });
}
const { exector } = cacheCfg;
// 处理并发,在请求还处于pending过程中就发起了相同的请求
// 拿第一个请求
if (exector.length === 1) {
const next = async () => {
try {
if (!exector.length) return;
const response = await promiseGenerator(params);
// 如果成功了,那么直接resolve掉剩余同样的请求
while (exector.length) { // 清空
exector.shift().resolve(response);
}
// 缓存结果
cacheCfg.hit = response;
} catch (error) {
// 如果失败了 那么这个promise的则为reject
const { reject } = exector.shift();
reject(error);
next(); // 失败重试,降级为串行
}
};
next();
}
});
};
};
Тестовый кешАсинхронный
Сценарии для тестирования
- Интерфейс запроса случайным образом завершается успешно или неудачно
- Успешный ожидаемый результат, остальные запросы не будут выдаваться
- Повторите попытку в случае сбоя, затем отправьте следующий запрос
Быстро собрать сервер:
const koa = require("koa");
const app = new koa();
function sleep(seconds) {
return new Promise((resolve, reject) => {
setTimeout(resolve, seconds);
});
}
app.use(async (ctx, next) => {
if (ctx.url === "/test") {
await sleep(200);
const n = Math.random();
// 随机挂掉接口
if (n > 0.8) {
ctx.body = n;
} else {
ctx.status = 404
ctx.body = ''
}
next();
}
});
app.listen(3000, "127.0.0.1", () =>
console.log("listening on 127.0.0.1:3000")
);
клиент
var fetch2 = cacheAsync(fetchData, "test2");
async function fetchData(a) {
const data = await fetch("//127.0.0.1:3000/test");
const d = await data.json();
console.log(d);
return d;
}
// 并发6个相同的请求
console.log(fetch2(2));
console.log(fetch2(2));
console.log(fetch2(2));
console.log(fetch2(2));
console.log(fetch2(2));
console.log(fetch2(2));
Посмотрите на результаты теста и обновите следующую страницу
В первый раз очень повезло, запрос прошел успешно в первый раз, и был отправлен только один запрос
Второй тест - неудача, последний запрос прошел успешно, и это тоже худший сценарий.
Третий тест, запрос успешно выполнен в третий раз
тестовый кешАктивно запросить в консолиfetch2
Успешный удар.
Судя по результатам теста, это правильно, в соответствии со сценариями параллелизма и кэширования. Некоторые люди спросят, например, почему вы хотите кэшировать интерфейсы. Когда вы вводите поиск по ключевому слову, событие ввода отслеживается.При добавлении или удалении ключевых слов будет сцена с теми же параметрами запроса.На данный момент это соответствует методу анти-тряски + интерфейс кеширование интерфейса. При обнаружении одного и того же ключевого слова извлекайте предыдущий кеш напрямую.
намекать
Поскольку этот кеш является методом закрытия, обновление кеша страницы также не удается. Однако я думаю, что это так, потому что в большинстве случаев обновление страницы приводит к сбросу состояния.Если вы хотите сохранить его, лучше сохранить его в локальном хранилище.