fxos-components / bridge

Exposes services between JavaScript contexts
https://fxos-components.github.io/bridge
MIT License
20 stars 8 forks source link

Simplify library #39

Closed wilsonpage closed 9 years ago

wilsonpage commented 9 years ago

Threads.js is becoming bloated, this is largely to do with the 'magic' we support under the hood, including:

This issue aims to discuss whether we should kill the Manager component, simplify the library and pass a little more responsibility over to the library user.


Right now the Manager is the middleman between Client and Service. In order to establish a connection we must go through the following steps:

  1. Message must be sent from a Client to the Manager
  2. The Manager must create the thread (eg. new Worker('foo.js'))
  3. The Manager waits for the Service to become 'ready'.
  4. The Manager forwards the connect request.
  5. The Service accepts the connection.
  6. The Manager receives confirmation of the connection
  7. The Manager responds to the Client to tell it it's connected.

During critical path of app startup, every millisecond counts. All this message passing is slow when the main thread is busy doing stuff like script parse, network requests, layout/paint, etc.

Deciding where the manager should live

It's tricky to know where the manager should live. Right now it has to live in the thread that persists for the lifetime of your app. With the 'content wrapper' that would have been the index.html. Now that the content wrapper is dying, it could be a dedicated client spawned via new ServiceWorker .openWindow() API. But that could take time to instantiate during app startup, and could severely punish our critical path.

If we kill Manager this problem goes away.

When we do have a reference

A lot of the time Clients have a direct reference to a thread. In these cases we can greatly optimize the connection flow by bypassing the Manager altogether.

var worker = new Worker('my-service.js');
var client = threads.client('my-service', worker);
var sharedworker = new SharedWorker('my-service.js');
var client = threads.client('my-service', sharedworker);
var iframe = document.querySelector('iframe');
var client = threads.client('my-service', iframe);

None of these use-cases depend on BroadcastChannel, which is good, as right now it's only in Gecko (bad for web).

When we don't have reference

There will still be cases when a thread reference is out of reach from a Client. For example a Client in a window wanting to connect to a Service in a Worker that has been spawned from a ServiceWorker.

We can still support this case. We can just omit the second argument, and let BroadcastChannel attempt to find the Service within a certain timeout period.

var client = threads.client('far-away-service');

What about contracts?

Contracts will have to be attached to the Service when it's created.

When do threads get terminated?

If the user is responsible for creating threads, they should probably be responsible for killing them. Alternatively we could .terminate() or port.close() when the user calls client.disconnect() and automatically call this on unload.

What about dynamic thread architecture (issue #12)?

By removing the Manager we remove the ability to dictate the thread architecture behind the scenes. IMO it would be too opinionated to go down this path, and add even more complex logic to an already large library (20kb minified). IMO it is the job of the browser engine to make optimizations based on devices capabilities, not the app's (more discussion in issue #12).

Summary

azasypkin commented 9 years ago

So just for the records, I'll dump here what I said on IRC:

azasypkin commented 9 years ago

Bringing in @steveck-chung who's also actively working with threads/bridge at the moment.

wilsonpage commented 9 years ago

I spoke with Vivien about this topic. His reasons for wanting to keep the Manger is because he wants to abstract the Service location from the code using the Client.

Sometimes Services may live in threads under completely different origins, in which case we would be interfacing with a something like navigator.connect() instead of Worker. If this complexity is kept inside the Manager, then we leave the options for type of endpoint open.

azasypkin commented 9 years ago

I spoke with Vivien about this topic. His reasons for wanting to keep the Manger is because he wants to abstract the Service location from the code using the Client.

Yeah, I'm not opposed to Manager per se either. I just think that Manager is a separate layer that should live in a separate lib: while threads.js provides bare, but powerful communication between different contexts (that is why we still call it a Bridge in Messages app :) ), Manager lib/plugin could provide smart way of managing those service origins.

I'm not sure about architectural requirements for the threads.js and can't see full picture, but I thought that potentially we can have more than one Manager, depending on environment/platform/requirement so that developer can write its own Manager if needed something smarter/dumber comparing to our default one (based on small/easy/high-performant threads/bridge.js).

Or maybe I'm just too skeptical about idea to have one-for-all magic lib, IMO it sounds good, handy and performmant on paper until we use it in real world scenario or in slightly different conditions :) We still can try!