UNPKG

5.29 kBJavaScriptView Raw
1"use strict";
2
3const Call = Function.prototype.call.bind(Function.prototype.call);
4const Apply = Function.prototype.call.bind(Function.prototype.apply);
5
6const ArrayReduce = Array.prototype.reduce;
7
8const I = require("immutable");
9const isIterable = I.Iterable.isIterable;
10const EmptyMap = I.Map();
11
12const program = require("@njudah/program");
13const AsynchronousRequest = require("./request");
14const AsynchronousResponse = AsynchronousRequest.AsynchronousResponse;
15
16const AsynchronousResponseEvent = I.Record({ responses: [] }, "AsynchronousResponseEvent");
17
18module.exports = function (aState, update, pull) {
19 const asynchronousCache = Object.create(null);
20 const push = program(aState, function (aState, anEvent) {
21 const state = anEvent instanceof AsynchronousResponseEvent ? handleAsynchronousResponseEvent(aState, anEvent, asynchronousCache) : aState;
22
23 return function exhaust(aState, anEvent) {
24 const updatedState = update(aState, anEvent);
25 const responseState = registerAsynchronousRequests(updatedState, pushAsynchronousResponse, asynchronousCache);
26
27 if (aState === responseState) //I.is(aState, responseState))
28 return aState;
29
30 return exhaust(responseState, {});
31 }(state, anEvent);
32 }, function (aState) {
33 pull(aState, getPendingAsynchronousFunctions(aState).size <= 0);
34 });
35 const pushAsynchronousResponse = getPushAsynchronousResponse(push);
36
37 return push;
38};
39
40function registerAsynchronousRequests(aState, pushAsynchronousResponse, asynchronousCache) {
41 const pendingAsynchronousFunctions = getPendingAsynchronousFunctions(aState);
42
43 return pendingAsynchronousFunctions.reduce(function (aState, anAsynchronousFunction, aUUID) {
44 if (!asynchronousCache[aUUID]) //Call(hasOwnProperty, asynchronousCache, aUUID))
45 {
46 asynchronousCache[aUUID] = {
47 cancel: anAsynchronousFunction.function(aResult => pushAsynchronousResponse(aUUID, AsynchronousResponse({ value: aResult })), aResult => pushAsynchronousResponse(aUUID, AsynchronousResponse({ isError: true, value: aResult })))
48 };
49
50 return aState;
51 }
52
53 if (!asynchronousCache[aUUID].response) //Call(hasOwnProperty, asynchronousCache[aUUID], "response"))
54 return aState;
55
56 const response = asynchronousCache[aUUID].response;
57
58 return serviceAsynchronousRequests(getPendingAsynchronousFunctions, aState, aUUID, response);
59 }, aState);
60}
61
62function getPushAsynchronousResponse(push, shouldCoallesce) {
63 var responses = null;
64
65 return function (aUUID, aResponse) {
66 if (responses) return responses[aUUID] = aResponse;
67
68 responses = { [aUUID]: aResponse };
69
70 setTimeout(function () {
71 const event = AsynchronousResponseEvent({ responses });
72 responses = null;
73 push(event);
74 }, 5);
75 };
76}
77
78function handleAsynchronousResponseEvent(anObject, anEvent, asynchronousCache) {
79 const responses = anEvent.responses;
80 const keys = Object.keys(responses);
81
82 return Call(ArrayReduce, keys, function (anObject, aKey) {
83 const response = responses[aKey];
84 const hasExistingResponse = !!asynchronousCache[aKey].response;
85
86 asynchronousCache[aKey].response = response;
87
88 return serviceAsynchronousRequests(hasExistingResponse ? getAsynchronousFunctions : getPendingAsynchronousFunctions, anObject, aKey, responses[aKey]);
89 }, anObject);
90}
91
92function serviceAsynchronousRequests(getAsynchronousFunctions, anObject, aUUID, aResponse) {
93 if (!getAsynchronousFunctions(anObject).has(aUUID)) return anObject;
94
95 if (anObject instanceof AsynchronousRequest) return anObject.function.UUID === aUUID ? anObject.set("response", aResponse) : anObject;
96
97 return anObject.reduce(function (anObject, aValue, aKey) {
98 const newValue = serviceAsynchronousRequests(getAsynchronousFunctions, aValue, aUUID, aResponse);
99
100 if (newValue !== aValue) return anObject.set(aKey, newValue);
101
102 return anObject;
103 }, anObject);
104}
105
106function getAsynchronousFunctions(anObject) {
107 return getCachedAsynchronousFunctions("__asynchronousFunctions", null, anObject);
108}
109
110function getPendingAsynchronousFunctions(anObject) {
111 return getCachedAsynchronousFunctions("__prendingAsynchronous", function (anAsynchronousRequest) {
112 return anAsynchronousRequest.response === null;
113 }, anObject);
114}
115
116function getCachedAsynchronousFunctions(aCacheKey, aPredicate, anObject) {
117 if (!anObject || anObject.__asynchronousIgnore) return EmptyMap;
118
119 if (anObject[aCacheKey]) return anObject[aCacheKey];
120
121 if (!isIterable(anObject)) return EmptyMap;
122
123 return anObject[aCacheKey] = function () {
124 if (anObject instanceof AsynchronousRequest) return !aPredicate || aPredicate(anObject) ? I.Map({ [anObject.function.UUID]: anObject.function }) : EmptyMap;
125
126 return anObject.reduce(function (aMap, aValue) {
127 const functions = getCachedAsynchronousFunctions(aCacheKey, aPredicate, aValue);
128
129 if (functions === EmptyMap) return aMap;
130
131 if (aMap === EmptyMap) return functions;
132
133 return aMap.merge(functions);
134 }, EmptyMap);
135 }();
136}
\No newline at end of file