@@ -188,19 +188,19 @@ export async function initPyodide(packages?: PyodidePackageInfo[], packageVersio
188188 }
189189
190190 // Already initialized with same versions
191- let state : { status : string } = { status : 'idle' } ;
192- pyodideState . subscribe ( ( s ) => ( state = s ) ) ( ) ;
193- if ( state . status === 'ready' ) {
191+ if ( worker ) {
194192 return ;
195193 }
196194
197195 initPromise = new Promise < void > ( ( resolve , reject ) => {
196+ let settled = false ;
197+
198198 // Create worker
199199 worker = new Worker ( new URL ( './worker.ts' , import . meta. url ) , { type : 'module' } ) ;
200200 worker . onmessage = handleWorkerMessage ;
201201 worker . onerror = ( error ) => {
202202 setError ( error . message ) ;
203- reject ( new Error ( error . message ) ) ;
203+ if ( ! settled ) { settled = true ; reject ( new Error ( error . message ) ) ; }
204204 } ;
205205
206206 // Wait for ready message
@@ -211,10 +211,10 @@ export async function initPyodide(packages?: PyodidePackageInfo[], packageVersio
211211 worker ! . onmessage = originalHandler ;
212212 initializedPackages = packages ?? null ;
213213 initializedVersions = packageVersions ?? null ;
214- resolve ( ) ;
214+ if ( ! settled ) { settled = true ; resolve ( ) ; }
215215 } else if ( event . data . type === 'error' && ! event . data . id ) {
216216 worker ! . onmessage = originalHandler ;
217- reject ( new Error ( event . data . error ) ) ;
217+ if ( ! settled ) { settled = true ; reject ( new Error ( event . data . error ) ) ; }
218218 }
219219 } ;
220220
@@ -224,7 +224,8 @@ export async function initPyodide(packages?: PyodidePackageInfo[], packageVersio
224224
225225 // Set timeout
226226 setTimeout ( ( ) => {
227- if ( state . status === 'loading' ) {
227+ if ( ! settled ) {
228+ settled = true ;
228229 const error = ERROR_MESSAGES . EXECUTION_TIMEOUT ;
229230 setError ( error ) ;
230231 reject ( new Error ( error ) ) ;
@@ -293,10 +294,15 @@ export async function execute(
293294/**
294295 * Reset the Python namespace
295296 * Clears all user-defined variables but keeps common imports (np, plt)
297+ * Only sends reset if the worker is alive and initialized
296298 */
297- export async function reset ( ) : Promise < void > {
298- await initPyodide ( ) ;
299- send ( { type : 'reset' } ) ;
299+ export function reset ( ) : void {
300+ if ( ! worker || ! initPromise ) return ;
301+ try {
302+ send ( { type : 'reset' } ) ;
303+ } catch {
304+ // Worker may have been terminated between our check and the send
305+ }
300306}
301307
302308/**
@@ -318,7 +324,19 @@ export function terminate(): void {
318324 initPromise = null ;
319325 initializedPackages = null ;
320326 initializedVersions = null ;
327+
328+ // Resolve all pending executions with an error so callers don't hang forever
329+ for ( const [ id , pending ] of pendingExecutions ) {
330+ pending . resolve ( {
331+ stdout : pending . stdout . join ( '\n' ) ,
332+ stderr : pending . stderr . join ( '\n' ) ,
333+ plots : pending . plots ,
334+ error : { message : 'Worker terminated' } ,
335+ duration : Date . now ( ) - pending . startTime
336+ } ) ;
337+ }
321338 pendingExecutions . clear ( ) ;
339+
322340 updateState ( { status : 'idle' , progress : '' , error : null } ) ;
323341}
324342
0 commit comments