MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1class ReactPromise {
2 constructor(status, value, reason) {
3 this.status = status;
4 this.value = value;
5 this.reason = reason;
6 }
7}
8
9function createPendingChunk() {
10 return new ReactPromise("pending", null, null);
11}
12
13function createResolvedIteratorResultChunk(response, value, done) {
14 return new ReactPromise(
15 "resolved_model",
16 (done ? "{\"done\":true,\"value\":" : "{\"done\":false,\"value\":") + value + "}",
17 response
18 );
19}
20
21function resolveModelChunk(response, chunk, value) {
22 if ("pending" !== chunk.status) {
23 chunk.reason.enqueueModel(value);
24 return;
25 }
26
27 chunk.status = "resolved_model";
28 chunk.value = value;
29 chunk.reason = response;
30}
31
32function resolveIteratorResultChunk(response, chunk, value, done) {
33 resolveModelChunk(
34 response,
35 chunk,
36 (done ? "{\"done\":true,\"value\":" : "{\"done\":false,\"value\":") + value + "}"
37 );
38}
39
40function startAsyncIterable(response, id, iterator) {
41 const buffer = [];
42 let closed = false;
43 let nextWriteIndex = 0;
44 const iterable = {};
45
46 iterable[Symbol.asyncIterator] = function () {
47 let nextReadIndex = 0;
48 return {
49 next(arg) {
50 if (arg !== undefined) throw new Error("bad arg");
51 if (nextReadIndex === buffer.length) {
52 if (closed) {
53 return new ReactPromise("fulfilled", { done: true, value: undefined }, null);
54 }
55 buffer[nextReadIndex] = createPendingChunk(response);
56 }
57 return buffer[nextReadIndex++];
58 }
59 };
60 };
61
62 const controller = {
63 enqueueValue(value) {
64 if (nextWriteIndex === buffer.length) {
65 buffer[nextWriteIndex] = new ReactPromise("fulfilled", { done: false, value }, null);
66 } else {
67 const chunk = buffer[nextWriteIndex];
68 chunk.status = "fulfilled";
69 chunk.value = { done: false, value };
70 chunk.reason = null;
71 }
72 nextWriteIndex++;
73 },
74 enqueueModel(value) {
75 if (nextWriteIndex === buffer.length) {
76 buffer[nextWriteIndex] = createResolvedIteratorResultChunk(response, value, false);
77 } else {
78 resolveIteratorResultChunk(response, buffer[nextWriteIndex], value, false);
79 }
80 nextWriteIndex++;
81 },
82 close(value) {
83 if (!closed) {
84 closed = true;
85 if (nextWriteIndex === buffer.length) {
86 buffer[nextWriteIndex] = createResolvedIteratorResultChunk(response, value, true);
87 } else {
88 resolveIteratorResultChunk(response, buffer[nextWriteIndex], value, true);
89 }
90 nextWriteIndex++;
91 }
92 },
93 error(error) {
94 throw error;
95 }
96 };
97
98 response._chunks.set(id, new ReactPromise("fulfilled", iterator ? iterable[Symbol.asyncIterator]() : iterable, controller));
99 return { buffer, controller };
100}
101
102function log(label, fn) {
103 try {
104 const out = fn();
105 console.log(`${label}:${out}`);
106 } catch (error) {
107 console.log(`${label}:ERR:${error && error.message ? error.message : String(error)}`);
108 }
109}
110
111const response = { _chunks: new Map() };
112const id = 1;
113
114startAsyncIterable(response, id, true);
115const chunk = response._chunks.get(id);
116
117log("reasonType", () => typeof chunk.reason);
118log("enqueueModelType", () => typeof chunk.reason.enqueueModel);
119
120resolveModelChunk(response, chunk, "\"1\"");
121console.log(`bufferLength:${chunk.reason ? "controller" : "missing"}`);
122
123const response2 = { _chunks: new Map() };
124const id2 = 2;
125response2._chunks.set(id2, new ReactPromise("resolved_model", "\"boot\"", response2));
126const badChunk = response2._chunks.get(id2);
127log("badReasonType", () => typeof badChunk.reason);
128log("badEnqueueModelType", () => typeof badChunk.reason.enqueueModel);
129log("badResolve", () => {
130 resolveModelChunk(response2, badChunk, "\"next\"");
131 return "ok";
132});