Skip to content

Commit 783858a

Browse files
Copilotna-trium-144
andcommitted
Refactor workers to stream output immediately via callbacks instead of accumulating in arrays
Co-authored-by: na-trium-144 <[email protected]>
1 parent d1e56fe commit 783858a

File tree

4 files changed

+114
-78
lines changed

4 files changed

+114
-78
lines changed

app/terminal/worker/jsEval.worker.ts

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,31 @@ function format(...args: unknown[]): string {
1010
// https://nodejs.org/api/util.html#utilformatformat-args
1111
return args.map((a) => (typeof a === "string" ? a : inspect(a))).join(" ");
1212
}
13-
let jsOutput: ReplOutput[] = [];
13+
let currentOutputCallback: ((output: ReplOutput) => void) | null = null;
1414

1515
// Helper function to capture console output
1616
const originalConsole = self.console;
1717
self.console = {
1818
...originalConsole,
1919
log: (...args: unknown[]) => {
20-
jsOutput.push({ type: "stdout", message: format(...args) });
20+
if (currentOutputCallback) {
21+
currentOutputCallback({ type: "stdout", message: format(...args) });
22+
}
2123
},
2224
error: (...args: unknown[]) => {
23-
jsOutput.push({ type: "stderr", message: format(...args) });
25+
if (currentOutputCallback) {
26+
currentOutputCallback({ type: "stderr", message: format(...args) });
27+
}
2428
},
2529
warn: (...args: unknown[]) => {
26-
jsOutput.push({ type: "stderr", message: format(...args) });
30+
if (currentOutputCallback) {
31+
currentOutputCallback({ type: "stderr", message: format(...args) });
32+
}
2733
},
2834
info: (...args: unknown[]) => {
29-
jsOutput.push({ type: "stdout", message: format(...args) });
35+
if (currentOutputCallback) {
36+
currentOutputCallback({ type: "stdout", message: format(...args) });
37+
}
3038
},
3139
};
3240

@@ -73,65 +81,68 @@ async function replLikeEval(code: string): Promise<unknown> {
7381
}
7482
}
7583

76-
async function runCode(code: string): Promise<{
77-
output: ReplOutput[];
84+
async function runCode(
85+
code: string,
86+
onOutput: (output: ReplOutput) => void
87+
): Promise<{
7888
updatedFiles: Record<string, string>;
7989
}> {
90+
currentOutputCallback = onOutput;
8091
try {
8192
const result = await replLikeEval(code);
82-
jsOutput.push({
93+
onOutput({
8394
type: "return",
8495
message: inspect(result),
8596
});
8697
} catch (e) {
8798
originalConsole.log(e);
8899
// TODO: stack trace?
89100
if (e instanceof Error) {
90-
jsOutput.push({
101+
onOutput({
91102
type: "error",
92103
message: `${e.name}: ${e.message}`,
93104
});
94105
} else {
95-
jsOutput.push({
106+
onOutput({
96107
type: "error",
97108
message: `${String(e)}`,
98109
});
99110
}
111+
} finally {
112+
currentOutputCallback = null;
100113
}
101114

102-
const output = [...jsOutput];
103-
jsOutput = []; // Clear output
104-
105-
return { output, updatedFiles: {} as Record<string, string> };
115+
return { updatedFiles: {} as Record<string, string> };
106116
}
107117

108118
function runFile(
109119
name: string,
110-
files: Record<string, string>
111-
): { output: ReplOutput[]; updatedFiles: Record<string, string> } {
120+
files: Record<string, string>,
121+
onOutput: (output: ReplOutput) => void
122+
): { updatedFiles: Record<string, string> } {
112123
// pyodide worker などと異なり、複数ファイルを読み込んでimportのようなことをするのには対応していません。
124+
currentOutputCallback = onOutput;
113125
try {
114126
self.eval(files[name]);
115127
} catch (e) {
116128
originalConsole.log(e);
117129
// TODO: stack trace?
118130
if (e instanceof Error) {
119-
jsOutput.push({
131+
onOutput({
120132
type: "error",
121133
message: `${e.name}: ${e.message}`,
122134
});
123135
} else {
124-
jsOutput.push({
136+
onOutput({
125137
type: "error",
126138
message: `${String(e)}`,
127139
});
128140
}
141+
} finally {
142+
currentOutputCallback = null;
129143
}
130144

131-
const output = [...jsOutput];
132-
jsOutput = []; // Clear output
133-
134-
return { output, updatedFiles: {} as Record<string, string> };
145+
return { updatedFiles: {} as Record<string, string> };
135146
}
136147

137148
async function checkSyntax(
@@ -162,8 +173,6 @@ async function checkSyntax(
162173

163174
async function restoreState(commands: string[]): Promise<object> {
164175
// Re-execute all previously successful commands to restore state
165-
jsOutput = []; // Clear output for restoration
166-
167176
for (const command of commands) {
168177
try {
169178
replLikeEval(command);
@@ -173,7 +182,6 @@ async function restoreState(commands: string[]): Promise<object> {
173182
}
174183
}
175184

176-
jsOutput = []; // Clear any output from restoration
177185
return {};
178186
}
179187

app/terminal/worker/pyodide.worker.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const PYODIDE_CDN = `https://cdn.jsdelivr.net/pyodide/v${pyodideVersion}/full/`;
1616
const HOME = `/home/pyodide/`;
1717

1818
let pyodide: PyodideInterface;
19-
let pyodideOutput: ReplOutput[] = [];
19+
let currentOutputCallback: ((output: ReplOutput) => void) | null = null;
2020

2121
// Helper function to read all files from the Pyodide file system
2222
function readAllFiles(): Record<string, string> {
@@ -48,12 +48,16 @@ async function init(
4848

4949
pyodide.setStdout({
5050
batched: (str: string) => {
51-
pyodideOutput.push({ type: "stdout", message: str });
51+
if (currentOutputCallback) {
52+
currentOutputCallback({ type: "stdout", message: str });
53+
}
5254
},
5355
});
5456
pyodide.setStderr({
5557
batched: (str: string) => {
56-
pyodideOutput.push({ type: "stderr", message: str });
58+
if (currentOutputCallback) {
59+
currentOutputCallback({ type: "stderr", message: str });
60+
}
5761
},
5862
});
5963

@@ -62,17 +66,20 @@ async function init(
6266
return { capabilities: { interrupt: "buffer" } };
6367
}
6468

65-
async function runCode(code: string): Promise<{
66-
output: ReplOutput[];
69+
async function runCode(
70+
code: string,
71+
onOutput: (output: ReplOutput) => void
72+
): Promise<{
6773
updatedFiles: Record<string, string>;
6874
}> {
6975
if (!pyodide) {
7076
throw new Error("Pyodide not initialized");
7177
}
78+
currentOutputCallback = onOutput;
7279
try {
7380
const result = await pyodide.runPythonAsync(code);
7481
if (result !== undefined) {
75-
pyodideOutput.push({
82+
onOutput({
7683
type: "return",
7784
message: String(result),
7885
});
@@ -86,7 +93,7 @@ async function runCode(code: string): Promise<{
8693
const execLineIndex = lines.findIndex((line) =>
8794
line.includes("<exec>")
8895
);
89-
pyodideOutput.push({
96+
onOutput({
9097
type: "error",
9198
message: lines
9299
.slice(0, 1)
@@ -95,33 +102,35 @@ async function runCode(code: string): Promise<{
95102
.trim(),
96103
});
97104
} else {
98-
pyodideOutput.push({
105+
onOutput({
99106
type: "error",
100107
message: `予期せぬエラー: ${e.message.trim()}`,
101108
});
102109
}
103110
} else {
104-
pyodideOutput.push({
111+
onOutput({
105112
type: "error",
106113
message: `予期せぬエラー: ${String(e).trim()}`,
107114
});
108115
}
116+
} finally {
117+
currentOutputCallback = null;
109118
}
110119

111120
const updatedFiles = readAllFiles();
112-
const output = [...pyodideOutput];
113-
pyodideOutput = []; // 出力をクリア
114121

115-
return { output, updatedFiles };
122+
return { updatedFiles };
116123
}
117124

118125
async function runFile(
119126
name: string,
120-
files: Record<string, string>
121-
): Promise<{ output: ReplOutput[]; updatedFiles: Record<string, string> }> {
127+
files: Record<string, string>,
128+
onOutput: (output: ReplOutput) => void
129+
): Promise<{ updatedFiles: Record<string, string> }> {
122130
if (!pyodide) {
123131
throw new Error("Pyodide not initialized");
124132
}
133+
currentOutputCallback = onOutput;
125134
try {
126135
// Use Pyodide FS API to write files to the file system
127136
for (const filename of Object.keys(files)) {
@@ -142,7 +151,7 @@ async function runFile(
142151
const execLineIndex = lines.findLastIndex((line) =>
143152
line.includes("<exec>")
144153
);
145-
pyodideOutput.push({
154+
onOutput({
146155
type: "error",
147156
message: lines
148157
.slice(0, 1)
@@ -151,23 +160,23 @@ async function runFile(
151160
.trim(),
152161
});
153162
} else {
154-
pyodideOutput.push({
163+
onOutput({
155164
type: "error",
156165
message: `予期せぬエラー: ${e.message.trim()}`,
157166
});
158167
}
159168
} else {
160-
pyodideOutput.push({
169+
onOutput({
161170
type: "error",
162171
message: `予期せぬエラー: ${String(e).trim()}`,
163172
});
164173
}
174+
} finally {
175+
currentOutputCallback = null;
165176
}
166177

167178
const updatedFiles = readAllFiles();
168-
const output = [...pyodideOutput];
169-
pyodideOutput = []; // 出力をクリア
170-
return { output, updatedFiles };
179+
return { updatedFiles };
171180
}
172181

173182
async function checkSyntax(

0 commit comments

Comments
 (0)