@@ -26,6 +26,7 @@ export function useJavaScript(): RuntimeContext {
2626type MessageToWorker =
2727 | {
2828 type : "init" ;
29+ payload ?: undefined ;
2930 }
3031 | {
3132 type : "runJavaScript" ;
@@ -37,6 +38,7 @@ type MessageToWorker =
3738 }
3839 | {
3940 type : "restoreState" ;
41+ payload : { commands : string [ ] } ;
4042 } ;
4143
4244type MessageFromWorker =
@@ -59,7 +61,7 @@ export function JavaScriptProvider({ children }: { children: ReactNode }) {
5961 Map < number , [ ( payload : any ) => void , ( error : string ) => void ] >
6062 > ( new Map ( ) ) ;
6163 const nextMessageId = useRef < number > ( 0 ) ;
62- const isInterrupted = useRef < boolean > ( false ) ;
64+ const executedCommands = useRef < string [ ] > ( [ ] ) ;
6365
6466 function postMessage < T > ( { type, payload } : MessageToWorker ) {
6567 const id = nextMessageId . current ++ ;
@@ -86,70 +88,52 @@ export function JavaScriptProvider({ children }: { children: ReactNode }) {
8688 }
8789 } ;
8890
89- postMessage < InitPayloadFromWorker > ( {
91+ return postMessage < InitPayloadFromWorker > ( {
9092 type : "init" ,
9193 } ) . then ( ( { success } ) => {
9294 if ( success ) {
9395 setReady ( true ) ;
9496 }
97+ return worker ;
9598 } ) ;
96-
97- return worker ;
9899 } , [ ] ) ;
99100
100101 useEffect ( ( ) => {
101- const worker = initializeWorker ( ) ;
102+ let worker : Worker | null = null ;
103+ initializeWorker ( ) . then ( ( w ) => {
104+ worker = w ;
105+ } ) ;
102106
103107 return ( ) => {
104- worker . terminate ( ) ;
108+ worker ? .terminate ( ) ;
105109 } ;
106110 } , [ initializeWorker ] ) ;
107111
108112 const interrupt = useCallback ( async ( ) => {
109113 // Since we can't interrupt JavaScript execution directly,
110114 // we terminate the worker and restart it, then restore state
111- isInterrupted . current = true ;
112-
113- // Reject all pending callbacks before terminating
114- const error = "Worker interrupted" ;
115- messageCallbacks . current . forEach ( ( [ , reject ] ) => reject ( error ) ) ;
116- messageCallbacks . current . clear ( ) ;
117-
118- // Terminate the current worker
119- workerRef . current ?. terminate ( ) ;
120-
121- // Reset ready state
122- setReady ( false ) ;
123-
124- // Create a new worker
125- initializeWorker ( ) ;
126-
127- // Wait for initialization with timeout
128- const maxRetries = 50 ; // 5 seconds total
129- let retries = 0 ;
130-
131- await new Promise < void > ( ( resolve , reject ) => {
132- const checkInterval = setInterval ( ( ) => {
133- retries ++ ;
134- if ( retries > maxRetries ) {
135- clearInterval ( checkInterval ) ;
136- reject ( new Error ( "Worker initialization timeout" ) ) ;
137- return ;
138- }
139-
140- if ( workerRef . current ) {
141- // Try to restore state
142- postMessage < { success : boolean } > ( {
143- type : "restoreState" ,
144- } ) . then ( ( ) => {
145- clearInterval ( checkInterval ) ;
146- isInterrupted . current = false ;
147- resolve ( ) ;
148- } ) . catch ( ( ) => {
149- // Keep trying
150- } ) ;
151- }
152- } , 100 ) ;
115+ await mutex . current . runExclusive ( async ( ) => {
116+ // Reject all pending callbacks before terminating
117+ const error = "Worker interrupted" ;
118+ messageCallbacks . current . forEach ( ( [ , reject ] ) => reject ( error ) ) ;
119+ messageCallbacks . current . clear ( ) ;
120+
121+ // Terminate the current worker
122+ workerRef . current ?. terminate ( ) ;
123+
124+ // Reset ready state
125+ setReady ( false ) ;
126+
127+ // Create a new worker and wait for it to be ready
128+ await initializeWorker ( ) ;
129+
130+ // Restore state by re-executing previous commands
131+ if ( executedCommands . current . length > 0 ) {
132+ await postMessage < { success : boolean } > ( {
133+ type : "restoreState" ,
134+ payload : { commands : executedCommands . current } ,
135+ } ) ;
136+ }
153137 } ) ;
154138 } , [ initializeWorker ] ) ;
155139
@@ -169,13 +153,11 @@ export function JavaScriptProvider({ children }: { children: ReactNode }) {
169153 type : "runJavaScript" ,
170154 payload : { code } ,
171155 } ) ;
156+ // Save successfully executed command
157+ executedCommands . current . push ( code ) ;
172158 return output ;
173159 } catch ( error ) {
174- // If interrupted or worker was terminated, return appropriate message
175- if ( isInterrupted . current ) {
176- return [ { type : "error" , message : "実行が中断されました" } ] ;
177- }
178- // Handle other errors
160+ // Handle errors (including "Worker interrupted")
179161 if ( error instanceof Error ) {
180162 return [ { type : "error" , message : error . message } ] ;
181163 }
0 commit comments