123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- const EXPORTED_SYMBOLS = ["LinksCache"];
- // This should be slightly less than SYSTEM_TICK_INTERVAL as timer
- // comparisons are too exact while the async/await functionality will make the
- // last recorded time a little bit later. This causes the comparasion to skip
- // updates.
- // It should be 10% less than SYSTEM_TICK to update at least once every 5 mins.
- // https://github.com/mozilla/activity-stream/pull/3695#discussion_r144678214
- const EXPIRATION_TIME = 4.5 * 60 * 1000; // 4.5 minutes
- /**
- * Cache link results from a provided object property and refresh after some
- * amount of time has passed. Allows for migrating data from previously cached
- * links to the new links with the same url.
- */
- this.LinksCache = class LinksCache {
- /**
- * Create a links cache for a given object property.
- *
- * @param {object} linkObject Object containing the link property
- * @param {string} linkProperty Name of property on object to access
- * @param {array} properties Optional properties list to migrate to new links.
- * @param {function} shouldRefresh Optional callback receiving the old and new
- * options to refresh even when not expired.
- */
- constructor(linkObject, linkProperty, properties = [], shouldRefresh = () => {}) {
- this.clear();
- // Allow getting links from both methods and array properties
- this.linkGetter = options => {
- const ret = linkObject[linkProperty];
- return typeof ret === "function" ? ret.call(linkObject, options) : ret;
- };
- // Always migrate the shared cache data in addition to any custom properties
- this.migrateProperties = ["__sharedCache", ...properties];
- this.shouldRefresh = shouldRefresh;
- }
- /**
- * Clear the cached data.
- */
- clear() {
- this.cache = Promise.resolve([]);
- this.lastOptions = {};
- this.expire();
- }
- /**
- * Force the next request to update the cache.
- */
- expire() {
- delete this.lastUpdate;
- }
- /**
- * Request data and update the cache if necessary.
- *
- * @param {object} options Optional data to pass to the underlying method.
- * @returns {promise(array)} Links array with objects that can be modified.
- */
- async request(options = {}) {
- // Update the cache if the data has been expired
- const now = Date.now();
- if (this.lastUpdate === undefined ||
- now > this.lastUpdate + EXPIRATION_TIME ||
- // Allow custom rules around refreshing based on options
- this.shouldRefresh(this.lastOptions, options)) {
- // Update request state early so concurrent requests can refer to it
- this.lastOptions = options;
- this.lastUpdate = now;
- // Save a promise before awaits, so other requests wait for correct data
- this.cache = new Promise(async resolve => {
- // Allow fast lookup of old links by url that might need to migrate
- const toMigrate = new Map();
- for (const oldLink of await this.cache) {
- if (oldLink) {
- toMigrate.set(oldLink.url, oldLink);
- }
- }
- // Update the cache with migrated links without modifying source objects
- resolve((await this.linkGetter(options)).map(link => {
- // Keep original array hole positions
- if (!link) {
- return link;
- }
- // Migrate data to the new link copy if we have an old link
- const newLink = Object.assign({}, link);
- const oldLink = toMigrate.get(newLink.url);
- if (oldLink) {
- for (const property of this.migrateProperties) {
- const oldValue = oldLink[property];
- if (oldValue !== undefined) {
- newLink[property] = oldValue;
- }
- }
- } else {
- // Share data among link copies and new links from future requests
- newLink.__sharedCache = {};
- }
- // Provide a helper to update the cached link
- newLink.__sharedCache.updateLink = (property, value) => {
- newLink[property] = value;
- };
- return newLink;
- }));
- });
- }
- // Provide a shallow copy of the cached link objects for callers to modify
- return (await this.cache).map(link => link && Object.assign({}, link));
- }
- };
|