Как узнать, что веб-воркер был отключен? - PullRequest
1 голос
/ 14 июля 2020

Есть ли способ узнать, когда веб-воркер был прерван?

Вы можете сделать это: const worker = new Worker ('example.com/worker')

worker.terminate()

worker.postMessage('yo') // This still works, but the worker is gone. Why?

После того, как воркер был уволен, на самом деле нет хорошего способа узнать. Похоже, вы все еще можете публиковать сообщения, и все ваши слушатели событий по-прежнему активны. Можем ли мы взглянуть на объект worker и увидеть, был ли он завершен?

1 Ответ

0 голосов
/ 15 июля 2020

Нет никакого способа, кроме эксплойта ошибки в Firefox.

Событие не запускается, объект Worker не имеет никакого свойства, дающего нам знать, все должно просто потерпите неудачу тихо.

Единственным решением было бы переопределить оба Worker.terminate() и DedicatedWorkerGlobalScope.close(), чтобы они сообщали вам об этом.

Вот доказательство концепции:

class StateAwareWorker extends Worker {
  constructor( ...args ) {
    super( ...args );
    this.terminated = false;
    this.addEventListener( 'error', ( evt ) => {
      if( evt.message && evt.message.endsWith( "closing" ) ) {
        evt.stopImmediatePropagation(); // don't let other scripts know about it
        evt.preventDefault(); // don't verbose in console
        Object.defineProperty( this, "terminated", { value: true } );
      }
    } );
  }
  terminate() {
    Object.defineProperty( this, "terminated", { value: true } );
    Worker.prototype.terminate.call( this );
  }
}


const url = generateWorkerURL();

const closing_worker = new StateAwareWorker( url );
closing_worker.postMessage( 'close' ); // closing internally
setTimeout( ()=> {
  console.log( 'closed worker is terminated:', closing_worker.terminated );
}, 200 );

const terminated_worker = new StateAwareWorker( url );
terminated_worker.terminate(); // closing from here
setTimeout( ()=> {
  console.log( 'terminated worker is terminated:', terminated_worker.terminated );
}, 200 );

const open_worker = new StateAwareWorker( url );
// not closing
setTimeout( ()=> {
  console.log( 'keep-open worker is terminated:', open_worker.terminated );
}, 200 );

function generateWorkerURL() {
  const script = document.querySelector( "[type='worker-script']" );
  const blob = new Blob( [ script.textContent ], { type: "text/javascript" } );
  return URL.createObjectURL( blob );
}
<script type="worker-script">
  // override self.close top let the other-side know we're closing
  {
    const original = self.close;
    self.close = () => {
      setTimeout( () => original.call( self ), 0 );
      throw new Error( "closing" );
    };
  }

  // close the Worker at first message received
  onmessage = () => {
    self.close();
  };
</script>

И для любопытных, использующих Firefox, вот "эксплойт", о котором я говорил, но не используйте его, он может не работать в несколько релизов:

// current Firefox doesn't throw a DOMException 
// when the Worker holding the other PortMessage is terminated
// we can exploit this "bug" (?) to know if the Worker is terminted or not
function isTerminated( worker ) {
  try {
    worker.postMessage(()=>{});
    return true;
  }
  catch(e){ return false; }
}
console.warn( 'This demo works only on Firefox' );
const worker_url = URL.createObjectURL( new Blob() );
const worker = new Worker( worker_url );
console.log( isTerminated( worker ) ); // false
worker.terminate(); // would also work if closed from inside
console.log( isTerminated( worker ) ); // true
...