astoilkov / main-thread-scheduling

Fast and consistently responsive apps using a single function call
MIT License
1.29k stars 32 forks source link

Easier web workers #6

Closed 101arrowz closed 2 years ago

101arrowz commented 2 years ago

This is a cool idea for the future of web-apps, and it's nice to have a userland implementation before it's added to the standard! Since you said in the README to make an issue if there's an alternate solution to UI freezing, I'm making this issue to mention my package isoworker, which does some magic to make web workers easier to use. Most worker wrappers that allow you to avoid creating workers by hand have one key flaw: you can't easily copy anything from the main thread into the worker and must send over only a function. My package mitigates pretty much all the complexity of web workers: you can make long-running closures on a separate thread with virtually no restrictions, it almost works like calling the closure asynchronously on the main thread.

This package still seems better for cases where there is a lot of data being processed or constant communication is necessary, but isoworker can yield zero UI freezing by running on a separate thread.

astoilkov commented 2 years ago

I did take a look at isoworker and it looks cool. Give me a few days so I can try it in real-world scenarios. I also want to add more information about Web Workers as an alternative in general. Thanks!

tom-sherman commented 2 years ago

Maybe it would be possible to build an implementation of the Web Workers API but running on the main thread, backed by main-thread-scheduling.

astoilkov commented 2 years ago

@tom-sherman I'm not sure I understand you correctly. Can you elaborate a little more?

tom-sherman commented 2 years ago

worker.js:

self.addEventListener('message', doSomeExpensiveWork)

script.js:

class CooperativeWorker {
  constructor(workerPath) {
    this.module = fetch(workerPath).then(res => res.text())
  }

  async postMessage(msg) {
    await yieldOrContinue()
    ;(function (self) {
      eval(await this.module)
    })({ addEventListener: this.addWorkerListener })
    // post the message now
  }
}

const worker = new CooperativeWorker('./worker.js') 
worker.postMessage(someMessage)

Something like this? It's basically a Web Worker but running on the main thread.

astoilkov commented 2 years ago

Is your idea for the user to be able to port an existing Web Worker code and move it to the main thread?

tom-sherman commented 2 years ago

The user can choose to run worker.js in a separate thread (via new Worker()) or cooperatively on the main thread via new CooperativeWorker().

The code inside worker.js would be isomorphic in that it could run in both of these environments.

astoilkov commented 2 years ago

Ok, I got it. Thanks for the suggestion.

astoilkov commented 2 years ago

I've updated the readme to include a better explanation about Web Workers as an alternative. Currently, Web Workers are one of the main alternatives.

Thanks for the discussion. If you think there is more actionable items in the issue you can write in the issue so I can reopen it.