tor-browser

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

memoizableAction.js (2542B)


      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
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 import { asSettled } from "./async-value";
      6 import { validateContext } from "./context";
      7 
      8 /**
      9 * memoizableActon is a utility for actions that should only be performed
     10 * once per key. It is useful for loading sources
     11 *
     12 * For Example
     13 *
     14 * export const setItem = memoizeableAction(
     15 *   "setItem",
     16 *   {
     17 *     hasValue: ({ a }, { getState }) => hasItem(getState(), a),
     18 *     getValue: ({ a }, { getState }) => getItem(getState(), a),
     19 *     createKey: ({ a }) => a,
     20 *     action: ({ a }, thunkArgs) => doSetItem(a, thunkArgs)
     21 *   }
     22 * );
     23 *
     24 * @param {string} name
     25 * @param {object} options
     26 * @param {Function} options.getValue
     27 *   Gets the result from the redux store.
     28 * @param {Function} options.createKey
     29 *   Creates a key for the requests map.
     30 * @param {Function} options.action
     31 *   Kicks off the async work for the action.
     32 */
     33 export function memoizeableAction(name, { getValue, createKey, action }) {
     34  const requests = new Map();
     35  return args => async thunkArgs => {
     36    let result = asSettled(getValue(args, thunkArgs));
     37    if (!result) {
     38      const key = createKey(args, thunkArgs);
     39      if (!requests.has(key)) {
     40        requests.set(
     41          key,
     42          (async () => {
     43            try {
     44              await action(args, thunkArgs);
     45            } catch (e) {
     46              console.warn(`Action ${name} had an exception:`, e);
     47            } finally {
     48              requests.delete(key);
     49            }
     50          })()
     51        );
     52      }
     53 
     54      await requests.get(key);
     55 
     56      if (args.cx) {
     57        validateContext(thunkArgs.getState(), args.cx);
     58      }
     59 
     60      result = asSettled(getValue(args, thunkArgs));
     61      if (!result) {
     62        // Returning null here is not ideal. This means that the action
     63        // resolved but 'getValue' didn't return a loaded value, for instance
     64        // if the data the action was meant to store was deleted. In a perfect
     65        // world we'd throw a ContextError here or handle cancellation somehow.
     66        // Throwing will also allow us to change the return type on the action
     67        // to always return a promise for the getValue AsyncValue type, but
     68        // for now we have to add an additional '| null' for this case.
     69        return null;
     70      }
     71    }
     72 
     73    if (result.state === "rejected") {
     74      throw result.value;
     75    }
     76    return result.value;
     77  };
     78 }