简体   繁体   中英

How can i synchronize callbacks (from within the callback) called from different sources?

I have the problem with my ui-framework (openui5) synchronizing two different callbacks. One is a callback from a ui-event that a rendering is complete, the other is an asynchronous callback from a data-access (ajax-call), done by a used model class. The ui-event callback needs the data from the data-callback.

To make it simpler here a simplified code snipped which demonstrates the problem. I want, that always onRouteMatched-Code should execute code when onUpdateFinished has finished. In this example i have created the async scenario bei using my asyncInit-Method. This is only for creating an easy test-scenario. in reality this onUpdateFinished is called by my data-model class and onRouteMatched by another framework-class event.

let testClass = {

 myUser : "",

 // Method definition:
 asyncInit : function() {
   me = this;
   let timer1 = Math.round(Math.random()*10000);
   let timer2 = Math.round(Math.random()*10000);

   setTimeout(function(){ me.onUpdateFinished();} , timer1);
   setTimeout(function(){ me.onRouteMatched();} , timer2);
 },

 onUpdateFinished : function(oEvent) {
   console.warn("onUpdateFinished.....setting Data")
   this.myUser = 'doe';
 },

 onRouteMatched : function(oEvent) {
   // Event though this callback is 
   // called before onUpddate
   console.warn("onRouteMatched...., myData="+this.myUser);
 }

}
testClass.asyncInit();

In this example onUpdateFinished and onRouteMatched are called by setTimeout with timeout-value of 0-9 seconds. If onUpdateFinished is called first, everything is ok and user in onRouteMatched is "doe". If onRouteMatched is called before onUpdateFinished the user is "undefined".

A not very clean workaround is to use a "sync"-variable and to check it in onRouteMatched (see example). I also thougth about promisses, async and await and such stuff. But as i see, in all the examples i must have the control over initiating the callbacks in my code. but in this case i dont have influence about the callers of the callback.

Here example with my "workaround"

let testClass = {

 myUser : "",
 updOk  : false,

 // Method definition:
 asyncInit : function() {
   me = this;
   let timer1 = Math.round(Math.random()*10000);
   let timer2 = Math.round(Math.random()*10000);

   setTimeout(function(){ me.onUpdateFinished();} , timer1);
   setTimeout(function(){ me.onRouteMatched();} , timer2);
 },

 onUpdateFinished : function(oEvent) {
   console.warn("onUpdateFinished.....setting Data")
   this.myUser = 'doe';
   this.updOk = true;
 },

 onRouteMatched : function(oEvent) {
   // Event though this callback is 
   // called before onUpddate
   me = this;
   if (!this.updOk) {
     console.log("onRouteMatched called...");
     setTimeout(function(){ 
       console.warn("Update still not finished");
       me.onRouteMatched();
     },1000)
     return;
   }
   console.warn("onRouteMatched...., myData="+this.myUser);
 }

}
testClass.asyncInit();

Build up a promise around the user:

let userArrived, user = new Promise(resolve => userArrived = resolve);

Then, inside onUpdateFinished , resolve the promise:

onUpdateFinished : function(oEvent) {
  userArrived("John Doe");
}

Inside onRouteMatched consume the promise:

onRouteMatched : function(oEvent) {
  user.then(username => {
    console.log(`${username} is ready!`);
  });
}

Or using async / await that could even be written as:

async onRouteMatched(oEvent) {
  const username = await user;
  console.log(`${username} is ready!`);
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM