• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Event Chaining

Engaged ,
Jun 23, 2017 Jun 23, 2017

Copy link to clipboard

Copied

Hi community,

I need to understand what I'm conceptually doing wrong or what I'm confusing here.

This experiment is to test the chaining of events, so basically Event1 shall trigger Event2, which is split, so Event2 shall trigger Event3 and then finish. Sounds a bit weird when describing it that way, so here's the basic code:

index.html:

<html>

  <head>

    <meta charset="utf-8">

    <script src="./lib/CSInterface.js"></script>

    <script src="./lib/jquery.js"></script>

    <script>

    $(document).ready(function() {

        $("#EventChain").on("click", function(e){

            e.preventDefault();

            var cs = new CSInterface();

            var message = "Event Listeners created.\nLet's go!\n\n";

            cs.addEventListener("Test.Event1", function(evt) {

                message += evt.data + "\n\nEvent 2 to occur...\n\n";

                $("#textarea").text(message);

                // console.log(evt.data);

                cs.evalScript('$._ext_ed.e_chain2("This is Event 2.")');

            });

            cs.addEventListener("Test.Event2", function(evt) {

                message += evt.data + "\n\nEvent 3 to occur...\n\n";

                $("#textarea").text(message);

                // console.log(evt.data);

                cs.evalScript('$._ext_ed.e_chain3("This is Event 3.")');

            });

            cs.addEventListener("Test.Event3", function(evt) {

                message += evt.data + "\n\n";

                $("#textarea").text(message);

                // console.log(evt.data);

                cs.removeEventListener("Test.Event1");

                cs.removeEventListener("Test.Event2");

                cs.removeEventListener("Test.Event3");

            });

            $("#textarea").text(message);

            cs.evalScript('$._ext_ed.e_chain1("This is Event 1.")');

        });

    });

    </script>

</head>

<body>

<header></header>

<section>

  <button id="EventChain">EventChain</button><br/>

  <textarea id="textarea" placeholder="Click the EventChain button!"></textarea>

</section>

<footer></footer>

</body>

</html>

JSX:

try {

    var xLib = new ExternalObject("lib:\PlugPlugExternalObject");

} catch (e) {

    alert(e);

}

$._ext_ed={

    dispatchEventCEP : function (_type, _payload) {

        if (xLib) {

            var eventObj = new CSXSEvent();

            eventObj.type = _type;

            eventObj.data = _payload;

            eventObj.dispatch();

        } else {

            alert ("PlugPlugExternalObject not loaded.", true);           

        }

    },

   

    e_chain1 : function (msg) {

        alert ("Message \""+msg+"\" received.", false)

        var eventType = "Test.Event1";

        $._ext_ed.dispatchEventCEP(eventType, msg)

    },

   

    e_chain2 : function (msg) {

        var eventType = "Test.Event2";

        $._ext_ed.dispatchEventCEP(eventType, msg + "part one");

        $.sleep(5000);

        $._ext_ed.dispatchEventCEP(eventType, msg + "part two");

    },

   

    e_chain3 : function (msg) {

        var eventType = "Test.Event3";

        $._ext_ed.dispatchEventCEP(eventType, msg)

    }

}

expected test result:

Event Listeners created.

Let's go!

This is Event 1.

Event 2 to occur...

This is Event 2.part one

Event 3 to occur...

This is Event 3.

This is Event 2.part two

Event 3 to occur...

This is Event 3.

actual test result:

Event Listeners created.

Let's go!

This is Event 1.

Event 2 to occur...

This is Event 2.part one

Event 3 to occur...

This is Event 2.part two

Event 3 to occur...

This is Event 3.

This is Event 3.

What's going on? Why is Event 3 not completely dispatched when Event 2 part one is obviously finished?

It gets even worse when I click the button once more:

Event Listeners created.

Let's go!

This is Event 1.

Event 2 to occur...

This is Event 2.part one

Event 3 to occur...

This is Event 2.part two

Event 3 to occur...

This is Event 2.part one

Event 3 to occur...

This is Event 2.part two

Event 3 to occur...

This is Event 3.

This is Event 3.

This is Event 3.

This is Event 3.

This is Event 3.

This is Event 3.

This is Event 3.

This is Event 3.

I'll try to find some useful reading on this myself but I'd appreciate useful comments!

Cheers,

e.d.

TOPICS
SDK

Views

1.5K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jun 23, 2017 Jun 23, 2017

Copy link to clipboard

Copied

e.d.  wrote

What's going on? Why is Event 3 not completely dispatched when Event 2 part one is obviously finished?

I took a look through the code and believe I've isolated the issue. The location at which things seem to break for you is the moment you call $.sleep(5000);. I think I understand your intuition here: sleep should emulate what the browser setTimeout function does. Unfortunately, sleep appears to completely pause processing of the ExtendScript thread. From the ESTK's Object Model Viewer:

$.sleep (msecs: number )

Core JavaScript Classes

Suspends the calling thread for a number of milliseconds.

During a sleep period, checks at 100 millisecond intervals to see whether the sleep should be terminated. This can happen if there is a break request, or if the script timeout has expired.

msecs: Data Type: number

Number of milliseconds to sleep.

The key part is highlighted in bold red. Whereas setTimeout in browsers schedules the function to be called after a given delay, the ExtendScript sleep function will pause it entirely. This means that the events you throw to the ExtendScript context will not have a chance to execute before the second call to dispatchEventCEP in your e_chain2 function. By the time the ExtendScript processing thread has a chance to process the queued callbacks, both are already there waiting (one for over 5 seconds, the other likely on the order of milliseconds).

I should point out that all classes in the Premiere Pro ExtendScript API have an instance function called setTimeout with the following description:

App.setTimeout (eventName: string , function:any, milliseconds: number )

Adobe Premiere Pro CC 2017 (11.0) Object Model

eventName: Data Type: string

function: Data Type: any

milliseconds: Data Type: number

Perhaps there's a way that you could use one of these functions to "schedule" your calls in the delayed fashion that it seems you intended?

e.d.  wrote

It gets even worse when I click the button once more:

When you call cs.removeEventListener("EventName"), you are not actually removing the callback - it's most likely silently failing. You have to specify the function (and optionally the object) to remove the listener. To properly support this you should create named functions which you can specify in both add/removeEventListener calls, rather than anonymous inline functions.

The documentation for add/removeEventListener specifies that you could specify an "object containing the method handler" as a third argument, but I'm not sure if the handler function must still be specified or not (I'm guessing yes)...

I hope this is helpful!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jun 23, 2017 Jun 23, 2017

Copy link to clipboard

Copied

Hi sberic,

sberic  schrieb

e.d.   wrote

What's going on? Why is Event 3 not completely dispatched when Event 2 part one is obviously finished?

I took a look through the code and believe I've isolated the issue. The location at which things seem to break for you is the moment you call $.sleep(5000);. I think I understand your intuition here: sleep should emulate what the browser setTimeout function does. Unfortunately, sleep appears to completely pause processing of the ExtendScript thread. From the ESTK's Object Model Viewer:

$.sleep (msecs: number )

Core JavaScript Classes

Suspends the calling thread for a number of milliseconds.

During a sleep period, checks at 100 millisecond intervals to see whether the sleep should be terminated. This can happen if there is a break request, or if the script timeout has expired.

msecs: Data Type: number

Number of milliseconds to sleep.

The key part is highlighted in bold red. Whereas setTimeout in browsers schedules the function to be called after a given delay, the ExtendScript sleep function will pause it entirely. This means that the events you throw to the ExtendScript context will not have a chance to execute before the second call to dispatchEventCEP in your e_chain2 function. By the time the ExtendScript processing thread has a chance to process the queued callbacks, both are already there waiting (one for over 5 seconds, the other likely on the order of milliseconds).

thank you, that one I hadn't looked up as potential source of trouble... which reminds us: Never assume!

What's interesting though is that every blocking method (for instance also an alert dialog) suspends the entire event chain, which in my view should not happen! When I dispatch an event before the blocking happens, it should be triggering the handler, shouldn't it? What are your thoughts on this?

When you call cs.removeEventListener("EventName"), you are not actually removing the callback - it's most likely silently failing. You have to specify the function (and optionally the object) to remove the listener. To properly support this you should create named functions which you can specify in both add/removeEventListener calls, rather than anonymous inline functions.

The documentation for add/removeEventListener specifies that you could specify an "object containing the method handler" as a third argument, but I'm not sure if the handler function must still be specified or not (I'm guessing yes)...

You're mostly right, one needs to specify the handler on removal as well, which is why it's not a good idea to use an anonymous function. Again, this seems to be mandatory, the object seems to be optional, considering the CSInterface.js implementation.

Best,

e.d.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jun 23, 2017 Jun 23, 2017

Copy link to clipboard

Copied

e.d.  wrote

What's interesting though is that every blocking method (for instance also an alert dialog) suspends the entire event chain, which in my view should not happen! When I dispatch an event before the blocking happens, it should be triggering the handler, shouldn't it? What are your thoughts on this?

I am unclear as to what you specifically mean by "should be triggering the handler", but I will take a guess and say that you mean "calling a registered CEP listener when an ExtendScript CSXSEvent event is dispatched." To be honest: I have no idea.

Whether the event is triggered immediately simply queued up for later delivery when you call CSXSEvent.dispatch() is an implementation detail. Based on your explanation, I would guess that the function queues up the event for delivery during a separate processing phase on the thread. This would be similar to the way that calling setTimeout(func, 0) in a browser does not immediately call the function, even though it should have a 0ms delay - it is put in a queue for processing during the next frame. In the case of the CSXSEvent class, this would depend on the underlying implementation to which we do not have any insight. Perhaps Bruce Bullis​ could offer a more definitive answer as to what's going on with CSXSEvent.dispatch under the hood.

That said, my thoughts are that this is not-at-all surprising. You are likely adding an event to a queue but blocking the thread before the thread has a chance to process that queue. That would be my best guess. Basically: avoid blocking calls if you can!

e.d.  wrote

You're mostly right, one needs to specify the handler on removal as well, which is why it's not a good idea to use an anonymous function. Again, this seems to be mandatory, the object seems to be optional, considering the CSInterface.js implementation.

Well, the first paragraph I wrote says basically the exact same thing, simply worded differently. The second paragraph was pointing out a potential option to try as do not like to assume I understand something in the CEP/Premiere SDK, even when there's documentation.

The reason I wrote the second paragraph is because I could see a situation wherein Adobe implemented a way to "remove all event listeners for a given object and a given 'type' by calling cs.removeEventListener("someEvent", null, myObj);". I have actually written event systems that have just such a mechanism - even ones that support removing all registered listeners for a specified object, across all event "types".

That said, I will admit now that this makes far less sense given that the setup is mirrored in the cs.addEventListener function parameters and I can't see a way that it makes sense to call cs.addEventListener("someEvent", null, myObj); in a useful way. I've still not tested anything along these lines but I'd be willing to bet that calling the add/removeEventListener functions without specifying a function and specifying an object will simply fail silently. Some error handling either way (in this case or the one you encountered, with an undefined function handler) would probably be a big help in the future...

I'm glad my answer was helpful. I similarly hope that this one is.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jun 23, 2017 Jun 23, 2017

Copy link to clipboard

Copied

Actually, now I'm doubly confused. Based on your writeup, it looks like the CSXSEvent.dispatch() did succeed... the "Event 3 to occur..." message comes out before the "This is Event 2.part two" message, right? Without seeing the timing on how these appear, I'd guess that the five second wait occurs between those two messages, with the rest appearing almost instantly. Is this not the case? If so, then the blocking call to sleep() would be blocking the CEP side handler call - in your words: when [you] dispatch an event before the blocking happens, it [does trigger] the handler (before the block-induced pause).

Unless my initial guess as to what you were describing was of base...

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jun 24, 2017 Jun 24, 2017

Copy link to clipboard

Copied

Hi sberic,

no, the 5sec break happens after "Event 2 to occur...", so obviously the queue (if there is one) is affected by the blocking method as well, which indicates the JSX engine is single-threaded, right?

Bruce Bullis​: I noticed in the sample Panel the PlugPlugObject call is somewhat different, for Windows you're using

var eoName = new ExternalObject('lib:PlugPlugExternalObject.dll')

but it seems to make no difference if I use

var xLib = new ExternalObject('lib:\PlugPlugExternalObject')

-> built-in fault tolerance / error handling? Or legacy compatibility?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jun 24, 2017 Jun 24, 2017

Copy link to clipboard

Copied

e.d.  wrote

no, the 5sec break happens after "Event 2 to occur...", so obviously the queue (if there is one) is affected by the blocking method as well

Ahh, that was unclear in the original post! Given this bit of information, I'd say that "Yes, it does appear that the queue is affected."

e.d.  wrote

the JSX engine is single-threaded, right?

Yes. The JSX (ExtendScript) engine is effectively ECMAScript 262 3rd Edition + API Extensions. Under the hood, it actually "uses an older version of SpiderMonkey" (source). According to the SpiderMonkey documentation​ (emphasis mine):

All JS code and most JSAPI calls run within a JSContext. The JSContext can be thought of as a machine that knows how to run JavaScript code, or as an abstraction of the notion of a thread. Exception handling, for example, is per-JSContext. Each JSContext must be used by only one thread at a time.

Precisely how Adobe apps (e.g. Premiere Pro) process the queued events does remain unclear, but I'd be willing to bet that all messages are processed once the current script event is processed (recall that you're actually in an event handler when you call $.sleep()). What's more, the JavaScript concurrency model seems to suggest that only a single event can be processed at a time, which likely includes the processing (delivery?) of posted events.

At least this is what your test suggests... I think at this point you'd simply need an Adobe engineer who's familiar with the ExtendScript integration to tell you more definitively...

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Jun 27, 2017 Jun 27, 2017

Copy link to clipboard

Copied

> built-in fault tolerance / error handling? Or legacy compatibility?

That's what the code I was stealing did.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jun 27, 2017 Jun 27, 2017

Copy link to clipboard

Copied

Hi Bruce,

That's what the code I was stealing did.

what does that mean exactly? Can you please expand on this? Currently it's

ambiguous to me.

Generally speaking, my original question has not been answered. I would

appreciate concise information along lines like

"The engine will always queue events to be executed after the current

function's context has ended, which implies blocking calls will also block

event propagation. [...further consequences or implications or references

to standards documents...] ". Can you or someone else from your team please

provide this in a concise way?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Enthusiast ,
Jun 29, 2017 Jun 29, 2017

Copy link to clipboard

Copied

I see that you unmarked the post as "Answered". That's a bit surprising given that the information provided should have helped unlock you given the problem as stated:

What's going on? Why is Event 3 not completely dispatched when Event 2 part one is obviously finished?

The answer, of course, being that $.sleep() is a blocking call that stops the thread from sending the message. Theoretically, this should have allowed you to adjust your script to work around the issue.

(Not to mention your second question, which was similarly solved by that first post.)

That said, I DO fully understand your desire to get a definitive understanding of the underlying event scheduling and processing system - I just think it's a different question. I too am very interested to better understand how the event model is actually working under the hood (when processing of the 'scheduled/dispatched' events occurs). To that end I'll invoke the good name of Bruce Bullis​​ to hope that he can come in and answer this follow-up question (or bring someone in who can).

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Jun 29, 2017 Jun 29, 2017

Copy link to clipboard

Copied

Hey sberic,

I didn't do this in order to strip you of your merit (I appreciate your input), but to give a signal to Adobe there's action required here.

My suspicion is that questions which appear to be answered are not being looked into any more.

I'll revert this as soon as there is an answer from the Adobe team...

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe Employee ,
Jun 29, 2017 Jun 29, 2017

Copy link to clipboard

Copied

LATEST

[I've asked for expert input...]

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines