forked from RubyLouvre/anu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateResource.js
133 lines (121 loc) · 4.08 KB
/
createResource.js
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { noop } from "../src/util";
import { createContext } from "../src/createContext";
const Empty = 0;
const Pending = 1;
const Resolved = 2;
const Rejected = 3;
export function createResource(loadResource, hash) {
const resource = {
read(cache, key) {
if (hash === undefined) {
return cache.read(resource, key, loadResource, key);
}
const hashedKey = hash(key);
return cache.read(resource, hashedKey, loadResource, key);
},
preload(cache, key) {
if (hash === undefined) {
cache.preload(resource, key, loadResource, key);
return;
}
const hashedKey = hash(key);
cache.preload(resource, hashedKey, loadResource, key);
},
};
return resource;
}
let CACHE_TYPE = 0xcac4e;
export function createCache(invalidator) {
const resourceCache = new Map();
function getRecord(resourceType, key) {
let recordCache = resourceCache.get(resourceType);
if (recordCache !== undefined) {
const record = recordCache.get(key);
if (record !== undefined) {
return record;
}
} else {
recordCache = new Map();
resourceCache.set(resourceType, recordCache);
}
const record = {
status: Empty,
suspender: null,
value: null,
error: null,
};
recordCache.set(key, record);
return record;
}
function load(emptyRecord, suspender) {
const pendingRecord = emptyRecord;
pendingRecord.status = Pending;
pendingRecord.suspender = suspender;
suspender.then(
value => {
// Resource loaded successfully.
const resolvedRecord = pendingRecord;
resolvedRecord.status = Resolved;
resolvedRecord.suspender = null;
resolvedRecord.value = value;
},
error => {
// Resource failed to load. Stash the error for later so we can throw it
// the next time it's requested.
const rejectedRecord = pendingRecord;
rejectedRecord.status = Rejected;
rejectedRecord.suspender = null;
rejectedRecord.error = error;
}
);
}
const cache = {
invalidate() {
invalidator();
},
preload(resourceType, key, miss, missArg) {
const record = getRecord(resourceType, key);
switch (record.status) {
case Empty:
// Warm the cache.
var suspender = miss(missArg);
load(record, suspender);
return;
case Pending:
// There's already a pending request.
return;
case Resolved:
// The resource is already in the cache.
return;
case Rejected:
// The request failed.
return;
}
},
read(resourceType, key, miss, missArg) {
const record = getRecord(resourceType, key);
switch (record.status) {
case Empty:
// Load the requested resource.
var suspender = miss(missArg);
load(record, suspender);
throw suspender;
case Pending:
// There's already a pending request.
throw record.suspender;
case Resolved:
return record.value;
case Rejected:
default:
// The requested resource previously failed loading.
var error = record.error;
throw error;
}
},
};
cache.$$typeof = CACHE_TYPE;
return cache;
}
const globalCache = createCache(noop);
export const SimpleCache = createContext(globalCache);
// https://dev.to/swyx/a-walkthrough-of-that-react-suspense-demo--4j6ahttps://dev.to/swyx/a-walkthrough-of-that-react-suspense-demo--4j6a