how to render UI with multiplte service calls

172 Views Asked by At

I just have a general question for Javascript. If I have to invoke two services for a UI and those two services calls have their own call backs, but UI template has to be rendered only after both the callbacks have finished execution, what should be the best Javascript practice to do it?

invokeServices() {
    invokeService1(param1, param2, svcCallback1);
    invokeService2(param1, param2, svcCallback2);
    //where to render the template???
}

function svcCallback1 (){
    //where to render the template???

}

function svcCallback2 (){
    //where to render the template???

}
2

There are 2 best solutions below

1
On

This post might help: Marko vs React: An In-depth Look. Specifically, look for the section on Async for how to use promises and the <await/> tag to delay rendering until all the data is present.

import fsp from 'fs-promise';

$ var filePath = __dirname + '/hello.txt';
$ var readPromise = fsp.readFile(filePath, {encoding: 'utf8'});

<await(helloText from readPromise)>
  <p>
    ${helloText}
  </p>
</await>
0
On

1. Track Completion of Callbacks

function invokeServicesAndRender() {
  let remaining = 2;
  let service1Data;
  let service2Data;

  invokeService1(param1, param2, (err, data) => {
     service1Data = data;
     if (!--remaining) done();
  });

  invokeService2(param1, param2, (err, data) => {
     service2Data = data;
     if (!--remaining) done();
  });

  function done() {
     // all data is here now, we can render the ui
  }
}

2. Promisify and use Promise.all

Node has a built-in method to promisify callbacks: util.promisify

const promisify = require('util').promisify;
const promiseService1 = promisify(invokeService1);
const promiseService2 = promisify(invokeService2);

function invokeServicesAndRender() {
  Promise.all([
    promiseService1(param1, param2),
    promiseService2(param1, param2),
  ]).then(([service1Data, service2Data]) => {
    // all data is here now, we can render the ui
  });
}

3. If you're using Marko, render immediately and pass promises to the template

I see you tagged this question marko, so I'll mention that with Marko it is recommended to begin rendering immediately and only wait to render chunks that actually need the data. This allows you to flush out content to the user faster.

const promisify = require('util').promisify;
const promiseService1 = promisify(invokeService1);
const promiseService2 = promisify(invokeService2);

function invokeServicesAndRender() {
  let service1DataPromise = promiseService1(param1, param2);
  let service2DataPromise = promiseService2(param1, param2);
  template.render({ service1DataPromise, service2DataPromise });
}

In your template you can use the <await> tag to wait for the data where it is needed:

<h1>Hello World</h1>
<p>this content gets rendered immediately</p>

<await(service1Data from input.service1DataPromise)>
  <!-- render content here that needs the service data -->
</await>