tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

task-cache.js (2682B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 "use strict";
      5 
      6 const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
      7 
      8 /**
      9 * The `TaskCache` allows for re-using active tasks when spawning a second task
     10 * would simply duplicate work and is unnecessary. It maps from a task's unique
     11 * key to the promise of its result.
     12 */
     13 const TaskCache = (module.exports = class TaskCache {
     14  constructor() {
     15    this._cache = new Map();
     16  }
     17 
     18  /**
     19   * Get the promise keyed by the given unique `key`, if one exists.
     20   *
     21   * @param {Any} key
     22   * @returns {Promise<Any> | undefined}
     23   */
     24  get(key) {
     25    return this._cache.get(key);
     26  }
     27 
     28  /**
     29   * Put the task result promise in the cache and associate it with the given
     30   * `key` which must not already have an entry in the cache.
     31   *
     32   * @param {Any} key
     33   * @param {Promise<Any>} promise
     34   */
     35  put(key, promise) {
     36    assert(!this._cache.has(key), "We should not override extant entries");
     37 
     38    this._cache.set(key, promise);
     39  }
     40 
     41  /**
     42   * Remove the cache entry with the given key.
     43   *
     44   * @param {Any} key
     45   */
     46  remove(key) {
     47    assert(
     48      this._cache.has(key),
     49      `Should have an extant entry for key = ${key}`
     50    );
     51 
     52    this._cache.delete(key);
     53  }
     54 });
     55 
     56 /**
     57 * Create a new action-orchestrating task that is automatically cached. The
     58 * tasks themselves are responsible from removing themselves from the cache.
     59 *
     60 * @param {Function(...args) -> Any} getCacheKey
     61 * @param {Generator(...args) -> Any} task
     62 *
     63 * @returns Cacheable, Action-Creating Task
     64 */
     65 TaskCache.declareCacheableTask = function ({ getCacheKey, task }) {
     66  const cache = new TaskCache();
     67 
     68  return function (...args) {
     69    return async function ({ dispatch, getState }) {
     70      const key = getCacheKey(...args);
     71 
     72      const extantResult = cache.get(key);
     73      if (extantResult) {
     74        return extantResult;
     75      }
     76 
     77      // Ensure that we have our new entry in the cache *before* dispatching the
     78      // task!
     79      let resolve;
     80      cache.put(
     81        key,
     82        new Promise(r => {
     83          resolve = r;
     84        })
     85      );
     86 
     87      resolve(
     88        dispatch(async function () {
     89          try {
     90            args.push(() => cache.remove(key), dispatch, getState);
     91            return await task(...args);
     92          } catch (error) {
     93            // Don't perma-cache errors.
     94            if (cache.get(key)) {
     95              cache.remove(key);
     96            }
     97            throw error;
     98          }
     99        })
    100      );
    101 
    102      return cache.get(key);
    103    };
    104  };
    105 };