2016-06-15 2 views
0

Иногда вы хотите использовать один и тот же обещанный объект в несколько раз и в разных местах. Такие, как обещание для ресурса AJAX из URL.Как выполнить обещания кэширования при работе с новым API-интерфейсом?

Трудно кэшировать объект, потому что вы не знаете, когда он будет асинхронно доступен.

Это нормально, потому что вы можете использовать call .then() multiple times on a single promise, чтобы вы его кэшировали в первый раз, когда вам это нужно, и получите доступ к кешу в последующие моменты времени. И когда он будет доступен, все .then s будут выполнены.

Но когда обещание от the new fetch API, похоже, это не так просто ... потому что второе обещание связано.

fetch() сам возвращает обещание, и когда оно выполнено, вы должны получить доступ к телу ответа HTTP через второе обещание с использованием метода, такого как .blob() или .json(). Они входят в состав "Body mixin".

Vanilla пример того, как использовать fetch():

fetch(url).then(function(resp) { 
    return resp.json().then(function(json) { 
    // use the JSON 
    }); 
}); 

проблемы в том, что даже если вы можете назвать .then() несколько раз, вы не можете вызов функции, такие как .blob() и .json() несколько раз на один ответ.

Похоже, что результат .json() и т. Д. Также необходимо будет кэшировать, но это должно быть сложнее, поскольку он будет доступен только асинхронно. Ресурс может быть запрошен дважды до того, как будут решены два асинхронных обещания - состояние гонки.

Я что-то упустил? Есть ли простой способ кэшировать JSON с обещаниями и т.д., чем я не вижу? Кажется, я не могу найти это в сети.

+0

Есть ли причина, по которой вы не можете использовать переменную для ее хранения? – ReX357

+0

Где и когда переменная станет доступной с учетом асинхронности? – hippietrail

+0

Неясно, в какой области вам необходимо использовать эту переменную. Внутри вашего блока после вызова выборки вы можете определить переменную над оператором return и назначить ее в первом блоке указанного оператора return. Затем последующий оператор возврата внутри одного блока выборки мог получить доступ к содержимому этой переменной. Но еще раз, ваш вопрос был неясно, когда нужно получить доступ к этим кеш-результатам. Если это выходит за запрос на выборку и любую последующую цепочку, укажите, пожалуйста, так. – ReX357

ответ

0

Я обновленный и гораздо более краткий решения теперь, когда я понимаю обещание немного лучше. Я полностью не обращал внимания на сцепление через .then() в конце концов, и я понял, мое предыдущее решение фактически используется "The Deferred anti-pattern" ака http://taoofcode.net/promise-anti-patterns/

let cache = {}; 
 

 
function cachingFetchJSON(url) { 
 
    if (!cache.hasOwnProperty(url)) { 
 
    console.log('fetching from afar'); 
 
    cache[url] = fetch(url).then(resp => resp.json()); 
 
    } else { 
 
    console.log('fetching from cache'); 
 
    } 
 
    return cache[url]; 
 
} 
 

 
for (let i = 0; i < 5; ++i) { 
 
    cachingFetchJSON("//jsonplaceholder.typicode.com/albums/" + (Math.floor(Math.random() * 4) + 1)) 
 
    .then(val => console.log(val)) 
 
    .catch(c => console.error('caught', c)); 
 
}

А вот оригинальное решение, которое я придумал после публикации этого вопроса и обучения из всех ответов и комментариев:

Вместо того, чтобы просто кэшировать обещания, возвращенные как часть API fetch(), я создаю новое обещание, которое разрешено только с текстом JSON ret ret опирался на орган реагирования.

function fetchJSON(url) { 
 
    return new Promise((resolve, reject) => { 
 
    console.log('fetching from afar'); 
 
    fetch(url) 
 
     .then(resp => resp.json() 
 
     .then(json => resolve(json))) 
 
     .catch(reason => reject(reason)); 
 
    }); 
 
} 
 

 
let cache = {}; 
 

 
function cachingFetchJSON(url) { 
 
    if (!cache.hasOwnProperty(url)) 
 
    cache[url] = fetchJSON(url); 
 
    else 
 
    console.log('fetching from cache'); 
 
    return cache[url]; 
 
} 
 

 
for (let i = 0; i < 5; ++i) { 
 
    cachingFetchJSON("//jsonplaceholder.typicode.com/albums/" + (Math.floor(Math.random() * 4) + 1)) 
 
    .then(val => console.log(val)) 
 
    .catch(c => console.error('caught', c)); 
 
}

1

Я не знаком с обещанием es6, но вот основы, как я это делаю в angularJS, который является тем же концептуальным.

Вместо того чтобы возвратить обещание, данное fetch. Вы использовать проработаны один, это дает вам возможность решить, когда вы хотите:

  • Немедленно, если он находится в кэше
  • Когда ответ сервер готов и проанализирован.

Вот как я обрабатывать кэширование в обещание angluar в

var cache = [..]//fetching cache from somewhere using an API 
var deferred = $q.defer();//build an empty promise; 
if(cache.contains('myKey')){ 
    var data = cache.get('myKey'); 
    // wrap data in promise 
    deferred.resolve(data); 
}else{ 
    fetch(req).then(function(resp) { 
     resp.json().then(function(json) { 
      cache.put('myKey', json); 
      deferred.resolve(json);//resolve promise 
     }); 
    }); 
} 
return deferred.promise;//return the promise object on which you will be able to call `then` 

С ES6 обещание действительно близки друг угловых Вам необходимо иметь возможность адаптировать свой код, чтобы соответствовать.

+0

Итак, все это было бы внутри функции, правильно? За исключением, может быть, кеша ... – hippietrail

+0

Точно, может быть, даже кеш, если вы извлечете его из какой-то сторонней библиотеки ofrlocalStorage, которая управляет кешем для вас. – Walfrat

2

Вы не можете назвать .json() или .text() несколько раз на ответ, потому что любой из этих методов потребляет тело, но вы можете скопировать ответ, прежде чем использовать его, чтобы вы могли просто кэшировать обещание, но вместо того, чтобы делать:

cachedFetch.then(response => response.json()).then(console.log); 

Do:

cachedFetch.then(response => response.clone().json()).then(console.log); 

Или обернуть fetch вызов внутри функции, которая всегда возвращает respone клон:

doFetch().then(response => response.json()).then(console.log); 

Где:

var _cached; 
function doFetch() { 
    if (!_cached) { 
    _cached = fetch('/something'); 
    } 
    return _cached.then(r => r.clone()); 
} 
+0

На самом деле я заметил этот метод '.clone()', но я предположил, что это приведет к ненужному использованию ресурсов, поэтому не исследовал дальше. – hippietrail

+0

Кроме того, должны ли все ваши примеры использовать 'cachedFetch()' или должны ли первые два использовать 'fetch()'? Должен ли последний быть рекурсивным? Я не уверен, что вы сделали опечатку, или я что-то не понимаю. – hippietrail

+1

Nop. В первом примере я предполагаю, что вы спустили свой 'fetch()' calle в другом месте, и вы положили обещанное обещание в 'cachedFetch'. В последнем примере función делает все в россыпи. Во всяком случае, я изменил ответ, чтобы быть более читаемым. – Salva

0

на основе @Salva ответа, я написал gist, который использует lodash.memoize и response.clone().

import memoize from 'lodash.memoize'; 

const cache = memoize(req => { 
    return fetch(req).then(response => { 
     return response; 
    }); 
}, req => req.url); 

function cacheFetch(input, init) { 
    return cache(new Request(input, init)).then(response => response.clone()); 
} 

export default cacheFetch; 
Смежные вопросы