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