599
Views
5
Comments
[Reactive Web App] Block: OnReady triggered before the OnInitialize is fulfilled.
Question

Hi,


While building a Reactive Web App I got some issues and some unexpected behavior while debugging it.

Built a test screen with a block inside and assigned all events to them with the following code:

JavaScript1:

console.log('Screen1:OnInitialize1');

JavaScript2:

setTimeout(function(){ $resolve(); }, 5000);

JavaScript3:

console.log('Screen1:OnInitialize2');


And got the following output on the console:

Screen1:OnInitialize1
Screen1:OnInitialize2
Block1:OnInitialize1
Block1:OnReady1
Screen1:OnReady1
Block1:OnInitialize2
Block1:OnReady2
Block1:OnRender1
Screen1:OnReady2
Screen1:OnRender1
Block1:OnRender2
Screen1:OnRender2


So, the issue is that the block Block1:OnReady runs while the Block1:OnInitialize is not yet fulfilled. I would expect something like this:

await OnInitialize();
await OnReady();
await OnRender();

But clearly that's not what is happening, at least on the blocks. On the screens seems to be correct. 


By the way, the same also applies on the OnApplicationReady event. Copying the same code into there the Screen1:OnInitialize will run before the OnApplicationReady is fulfilled.


I don't expect at all for this to be the expected behavior so, please let me know if I'm wrong.

2021-03-30 06-52-52
Ali Amin

Hi Luis,

setTimeout(function(){...}, 0) simply queues the code to run once the current call stack is finished executing.

it's a asynchronous call and  will not execute concurrently.


Hope this help!!!.


Cheers,

Ali Amin


UserImage.jpg
Luís Gomes

Hi Ali Amin,


Yes, I'm fully aware of that, but when you have '$resolve();' inside of a Javascript block it will wait till it's called, in other works, until the promise is fulfilled, more info here.


Regarding the issue, in general terms we have this for the screen:

Screen1:OnInitialize1
Screen1:OnInitialize2
Screen1:OnReady1
Screen1:OnReady2
Screen1:OnRender1
Screen1:OnRender2

And this for the Block:

Block1:OnInitialize1
Block1:OnReady1
Block1:OnInitialize2
Block1:OnReady2
Block1:OnRender1
Block1:OnRender2

The code is exactly the same but on the block level the OnReady is fired before the OnInitialize is completed/fulfilled (Block1:OnInitialize2).


So, on the screen level the OnReady will only be fired once the OnInitialize is completed/fulfilled. This means that it will wait for the promises ($resolve() calls) on the Javascript blocks.


On the Block level, the OnReady event won't wait until the OnInitialize is completed/fulfilled (if only have sync code there of course it will).

I went over the documentation here and found this OnInitialize info:

In Blocks, including any server action calls or local storage operations in this flow might cause the render of the Block to start before this action finishes. You will have issues if you set any variables in this flow that affect the render.


It is not very clear, but in the end I think it's related with the issue reported. Not sure if it was intended to be like that or it's just how it was implemented... but I would expected the behavior on screen & blocks to be aligned and, at the moment, it's not.


2025-01-17 13-16-27
Igor Kirtak

I have just noticed the same behavior and it was very surprising to me. In my case, there is nothing uncommon happening in the handlers, except the local data access (which might be the problem). The reason why I noticed this was because the variable used in OnReady was set in OnInitialize but in OnReady it was empty. I had to debug to see it, and after finding this topic, I can confirm that OnInitialize starts before OnReady, but completes after it. This can not be intended, because it breaks the basic assumption that you can use variables assigned in OnInitialize.

UserImage.jpg
Stanislav Ploschansky

OnInit should not have any "slow" action, but OnReady to be executed definitely after OnInit finished!!!! Otherwise soon or later that could be a huge headache for any case.

2017-06-19 06-39-23
Valerij Gerasimov

The example with 3 JS blocks and $resolve() action works as it should from my point of view. 
The third block is executed after first and second blocks but asynchronously. So in spite of execution all 3 JS blocks inside the same client Outsystems action some part of the code are executed synchronously. Since there is action with promise the some part of code is placed to callback and executed after promise is resolve. So OnInitialize is completed after executing second block and nothing blocks to run OnReady action since OnInitialize action returned control.

I modified the second block as:
console.log('Screen1:OnInitialize1.1');
setTimeout(function(){ console.log('Screen1:OnInitialize1.2'); $resolve(); }, 5000);
console.log('Screen1:OnInitialize1.3');

and output is following:
Screen1:OnInitialize1
Screen1:OnInitialize1.1
Screen1:OnInitialize1.3
Screen1:OnInitialize1.2
Screen1:OnInitialize2

The 1.3 log before 1.2 means that setTimeout(function(){ console.log('Screen1:OnInitialize1.2'); $resolve(); }, 5000); doesn't block next line of the code until promise is resolved while OnInitialize2  after OnInitialize1.2  means that third block is executed after timeout but OnInitialize1.2 is executed in timer callback and OnInitialize2 in the promise callback.
Outsystems places all JS blocks to different functions and run them according to JS execution flow. The second JS block has Promise definition and timer with callback:

define("Valery.MainFlow.Test.mvc$controller.OnInitialize.JavaScript3JS", [], function () {

return function ($actions, $roles, $public) {

return new Promise(function ($resolve, $reject) {

console.log('Screen1:OnInitialize1.1');

setTimeout(function(){ console.log('Screen1:OnInitialize1.2'); $resolve(); }, 5000);

console.log('Screen1:OnInitialize1.3');

});

};

});

define("Valery.MainFlow.Test.mvc$controller.OnInitialize.JavaScript2JS", [], function () {

return function ($actions, $roles, $public) {

console.log('Screen1:OnInitialize2');

};

});

define("Valery.MainFlow.Test.mvc$controller.OnInitialize.JavaScript1JS", [], function () {

return function ($actions, $roles, $public) {

console.log('Screen1:OnInitialize1');

};

});
As result first timer callback is executed, next timer function resolve promise and promise callback is executed. What is inside promise callback? We can see that content of promise callback is the rest part of the client action namely third JS block.

In case of using local storage aggregate OS introduces promise as well. So all code after aggregate is executed in the callback asynchronously. 
It means that reading local storage in OnInitialize action is not reliable way to use result somewhere except UI because we don't know when result will be available. So only Fetch data action is reliable way to get data from local storage. In such case you have OnAfterFetch callback to process result just after it is ready in the promise callback.

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.