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!`);
},