ionic-team / ionic-framework

A powerful cross-platform UI toolkit for building native-quality iOS, Android, and Progressive Web Apps with HTML, CSS, and JavaScript.
https://ionicframework.com
MIT License
50.93k stars 13.52k forks source link

Unable to bootstrap an Ionic2 app in a web worker #5734

Closed jimitndiaye closed 7 years ago

jimitndiaye commented 8 years ago

Short description of the problem:

The current bootstrap process for Ionic2, whether via the @App decorator or through direct use of the ionicProviders function in https://github.com/driftyco/ionic/blob/2.0/ionic/config/bootstrap.ts makes it really difficult to run it in a web worker. They make direct use of the window and document objects which are not available in a web worker.

What behavior are you expecting?

Suggest the creation of an ionicProviders equivalent that uses abstracted ng2 primitives (e.g DomAdapter, MessageBus, etc) to obtain the needed information when running in a worker app.

Might need a message bus bridge internally to retrieve the necessary information from the render thread. FYI: https://github.com/angular/angular/blob/master/modules/angular2/docs/web_workers/web_workers.md

Steps to reproduce:

  1. Try to boot an Ionic2 app in a web worker
export function ionicProviders(args: any={}) {
  let platform = new Platform();
  let navRegistry = new NavRegistry(args.pages);

  var config = args.config;

  if (!(config instanceof Config)) {
    config = new Config(config);
  }

  platform.setUrl(window.location.href);
  platform.setUserAgent(window.navigator.userAgent);
  platform.setNavigatorPlatform(window.navigator.platform);
  platform.load();
  config.setPlatform(platform);

  let clickBlock = new ClickBlock();

  let events = new Events();
  let featureDetect = new FeatureDetect();

  setupDom(window, document, config, platform, clickBlock, featureDetect);
  bindEvents(window, document, platform, events);

  // prepare the ready promise to fire....when ready
  platform.prepareReady(config);

  return [
    IonicApp,
    provide(ClickBlock, {useValue: clickBlock}),
    provide(Config, {useValue: config}),
    provide(Platform, {useValue: platform}),
    provide(FeatureDetect, {useValue: featureDetect}),
    provide(Events, {useValue: events}),
    provide(NavRegistry, {useValue: navRegistry}),
    TapClick,
    Form,
    Keyboard,
    MenuController,
    Translate,
    ROUTER_PROVIDERS,
    provide(LocationStrategy, {useClass: HashLocationStrategy}),
    HTTP_PROVIDERS,
  ];
}

function setupDom(window, document, config, platform, clickBlock, featureDetect) {
  let bodyEle = document.body;
  let mode = config.get('mode');

  // if dynamic mode links have been added the fire up the correct one
  let modeLinkAttr = mode + '-href';
  let linkEle = document.head.querySelector('link[' + modeLinkAttr + ']');
  if (linkEle) {
    let href = linkEle.getAttribute(modeLinkAttr);
    linkEle.removeAttribute(modeLinkAttr);
    linkEle.href = href;
  }

  // set the mode class name
  // ios/md/wp
  bodyEle.classList.add(mode);

  // language and direction
  platform.setDir(document.documentElement.dir, false);
  platform.setLang(document.documentElement.lang, false);

  let versions = platform.versions();
  platform.platforms().forEach(platformName => {
    // platform-ios
    let platformClass = 'platform-' + platformName;
    bodyEle.classList.add(platformClass);

    let platformVersion = versions[platformName];
    if (platformVersion) {
      // platform-ios9
      platformClass += platformVersion.major;
      bodyEle.classList.add(platformClass);

      // platform-ios9_3
      bodyEle.classList.add(platformClass + '_' + platformVersion.minor);
    }
  });

  // touch devices should not use :hover CSS pseudo
  // enable :hover CSS when the "hoverCSS" setting is not false
  if (config.get('hoverCSS') !== false) {
    bodyEle.classList.add('enable-hover');
  }

  if (config.get('clickBlock')) {
    clickBlock.enable();
  }

  // run feature detection tests
  featureDetect.run(window, document);
}

/**
 * Bind some global events and publish on the 'app' channel
 */
function bindEvents(window, document, platform, events) {
  window.addEventListener('online', (ev) => {
    events.publish('app:online', ev);
  }, false);

  window.addEventListener('offline', (ev) => {
    events.publish('app:offline', ev);
  }, false);

  window.addEventListener('orientationchange', (ev) => {
    events.publish('app:rotated', ev);
  });

  // When that status taps, we respond
  window.addEventListener('statusTap', (ev) => {
    // TODO: Make this more better
    var el = document.elementFromPoint(platform.width() / 2, platform.height() / 2);
    if (!el) { return; }

    var content = closest(el, 'scroll-content');
    if (content) {
      var scrollTo = new ScrollTo(content);
      scrollTo.start(0, 0, 300, 0);
    }
  });

  // start listening for resizes XXms after the app starts
  setTimeout(function() {
    window.addEventListener('resize', function() {
      platform.windowResize();
    });
  }, 2000);
}

Ionic Version: 2.x

Browser & Operating System: Android / Chrome

Run ionic info from terminal/cmd prompt:


Your system information:

Cordova CLI: 6.0.0
Gulp version:  CLI version 3.9.1
Gulp local:   Local version 3.9.1
Ionic Version: 2.0.0-beta.2
Ionic CLI Version: 2.0.0-beta.19
Ionic App Lib Version: 2.0.0-beta.9
OS:
Node Version: v5.6.0
jimitndiaye commented 8 years ago

Ideally this would be baked into the underlying angular2 framework's DomAdapter which is currently Parse5DomAdapter, but as you can see the current implementation does not do that.

Maybe what could be done is to extend Parse5DomAdapter and override the methods needed for ionic2 (e.g getUserAgent) to get the relevant information via the message bus from the render thread which in turn could get it from the DOM.

Alternatively you could have the bootstrapper on the render thread get all the relevant platform info then send it to the ionic app as one json object during initialization.

adamdbradley commented 8 years ago

Either way there's code that will have to run on the UI thread, which is where all of our dom setup will have to be fired from. It's the config setup that can run on the UI thread, and all of that can run before ng2 even needs to be bootstrapped. From the config we need to figure out what to add to the body class and a few other things. We'll have to make sure that all of that is separated. Thanks for all the info!

jimitndiaye commented 8 years ago

You're welcome. Looks like alot of messy marshalling either way. I suspect send all necessary information in bulk from UI thread to worker app may be the most efficient, rather than multiple worker-to-ui thread requests.

frederikschubert commented 8 years ago

I would also like to run ionic in a web worker as it brings fairly big performance improvements. Is this planned for the final release of v2?

@adamdbradley ?

victorivens05 commented 8 years ago

+1 I find webworkers to be a pivotal implementation for ionic and absolutely would like to hear more about it.

frederikschubert commented 8 years ago

Now that there is the new ionicBootstrap function would it be possible to also provide two functions ionicBootstrapRender and ionicBootstrapWorker? The setup part for the platform and DOM could be executed in the former and the results could be send via the postMessage mechanism to the application. The application would then be bootstrapped as a result of this setup message.

masimplo commented 8 years ago

Is there any update on this? Has anyone successfully used web workers on an Ionic2 app? Would also love to know what are IonicTeam's plans for web worker support?

cmyip commented 8 years ago

Was able to implement webworker by copying the JS file into the www/build folder. anyone care to write a wrapper/hook to pull worker js files?

frederikschubert commented 8 years ago

How did you manage to bootstrap the ionic app in a web worker? They are accessing the Dom when bootstrapping which is not possible in a worker.

konsultaner commented 8 years ago

+1 to let ionic run in a web worker

sergiomilici commented 8 years ago

+1

jmsegrev commented 8 years ago

Is there any update on this?

jgw96 commented 7 years ago

Hello everyone! Thanks for the feature request. I'm going to move this issue over to our internal list of feature requests for evaluation. We are continually prioritizing all requests that we receive with outstanding issues. We are extremely grateful for your feedback, but it may not always be our next priority. I'll copy the issue back to this repository when we have begun implementation. Thanks!

jgw96 commented 7 years ago

This issue was moved to driftyco/ionic-feature-requests#24

Jbelmonte commented 7 years ago

Hello! Is there any chance to get any updates on this feature request? Is it accepted and planned to be released as part of a known milestone? Thanks.

ionitron-bot[bot] commented 6 years ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.