@@ -424,9 +424,6 @@ function getDocument(src = {}) {
424424
425425 Promise . all ( [ worker . promise , gpuPromise ] )
426426 . then ( function ( [ , hasGPU ] ) {
427- if ( task . destroyed ) {
428- throw new Error ( "Loading aborted" ) ;
429- }
430427 if ( worker . destroyed ) {
431428 throw new Error ( "Worker was destroyed" ) ;
432429 }
@@ -469,9 +466,6 @@ function getDocument(src = {}) {
469466 }
470467
471468 return workerIdPromise . then ( workerId => {
472- if ( task . destroyed ) {
473- throw new Error ( "Loading aborted" ) ;
474- }
475469 if ( worker . destroyed ) {
476470 throw new Error ( "Worker was destroyed" ) ;
477471 }
@@ -486,10 +480,18 @@ function getDocument(src = {}) {
486480 pagesMapper
487481 ) ;
488482 task . _transport = transport ;
483+
484+ if ( task . destroyed ) {
485+ // `destroy()` was called during the worker handshake; the orderly
486+ // shutdown (including the "Terminate" message) will be issued
487+ // through the transport once destroy resumes.
488+ throw new Error ( "Loading aborted" ) ;
489+ }
489490 messageHandler . send ( "Ready" , null ) ;
490491 } ) ;
491492 } )
492- . catch ( task . _capability . reject ) ;
493+ . catch ( task . _capability . reject )
494+ . finally ( task . _setupCapability . resolve ) ;
493495
494496 return task ;
495497}
@@ -515,6 +517,14 @@ class PDFDocumentLoadingTask {
515517 */
516518 _capability = Promise . withResolvers ( ) ;
517519
520+ /**
521+ * Resolves once the load-time setup chain has settled, regardless of
522+ * outcome; used by `destroy()` to wait until `_transport` is either set
523+ * or definitely never going to be.
524+ * @private
525+ */
526+ _setupCapability = Promise . withResolvers ( ) ;
527+
518528 /**
519529 * @private
520530 */
@@ -568,11 +578,25 @@ class PDFDocumentLoadingTask {
568578 */
569579 async destroy ( ) {
570580 this . destroyed = true ;
581+ // The setup chain rejects `_capability` with "Loading aborted" once the
582+ // load-time chain unwinds (see `getDocument`). Claim that rejection
583+ // here so it isn't reported as unhandled during the awaits below;
584+ // callers awaiting `task.promise` still see it.
585+ this . _capability . promise . catch ( ( ) => { } ) ;
571586
572587 try {
588+ // `_pendingDestroy` must be set synchronously, before any `await`,
589+ // so subsequent `PDFWorker.create()` calls on the shared `workerPort`
590+ // observe it and throw (see issue 16777).
573591 if ( this . _worker ?. port ) {
574592 this . _worker . _pendingDestroy = true ;
575593 }
594+ // Wait for the load-time setup chain to settle so `_transport` is set
595+ // (when applicable) before we tear down. This is what guarantees the
596+ // "Terminate" message gets sent through `WorkerTransport.destroy` if
597+ // `destroy` races with the initial worker handshake.
598+ await this . _setupCapability . promise ;
599+
576600 await this . _transport ?. destroy ( ) ;
577601 } catch ( ex ) {
578602 if ( this . _worker ?. port ) {
0 commit comments