End of event handler execution VS. End of (event handler execution + microtasks execution)

22 views
Skip to first unread message

Leon

unread,
May 12, 2017, 5:31:54 AM5/12/17
to blink-dev, chromi...@chromium.org, shi...@chromium.org, fal...@chromium.org
When EventTarget::DispatchEvent returned, does it indicate end of event handler execution? Or end of 'event handler execution + microtasks execution'?
From some tests I guess it would be the latter, if so how could we get the timing point right after end of event handler execution but before executing microtasks? Thanks!

Jeremy Roman

unread,
May 12, 2017, 11:12:52 AM5/12/17
to Leon, blink-dev, Chromium-dev, shi...@chromium.org, fal...@chromium.org
There isn't necessarily such a point.

function foo() { console.log('listener'); Promise.resolve().then(() => console.log('microtask')); }
document.body.addEventListener('click', () => foo());
document.body.addEventListener('click', () => foo());
document.body.addEventListener('click', () => foo());

Clicking yields:

listener
microtask
listener
microtask
listener
microtask

If dispatchEvent happens during script (or otherwise queues microtasks), then they will be aggregated accordingly, but that's not done as part of the event dispatch logic.

Leon

unread,
May 14, 2017, 11:47:28 PM5/14/17
to blink-dev, leon...@intel.com, chromi...@chromium.org, shi...@chromium.org, fal...@chromium.org
Thanks a lot for kindly answer.

Yeah user clicking will yield:
    listener
In such case, I want to get this time point, is there any available C++ API?
    microtask
In such case, EventTarget::DispatchEvent returns here.
    listener
    microtask

And, I suppose 
    xxx
    document.body.click();
    document.body.click();
    xxx
will yield:
    listener
In such case, EventTarget::DispatchEvent returns here.
    listener
    microtask
    microtask

Jeremy Roman

unread,
May 15, 2017, 10:47:30 AM5/15/17
to Leon, blink-dev, Chromium-dev, Makoto Shimazu, Matt Falkenhagen
On Sun, May 14, 2017 at 11:46 PM, Leon <leon...@intel.com> wrote:
Thanks a lot for kindly answer.

Yeah user clicking will yield:
    listener
In such case, I want to get this time point, is there any available C++ API?
    microtask
In such case, EventTarget::DispatchEvent returns here.

But it doesn't. If there are multiple listeners, they are all fired before event dispatch ends (except for stopPropagation etc). I don't believe we offer a way to get between the script execution and microtask execution, though you could add one (it's kinda deep, though). Microtasks are intended to be run as a part of entering script. But even then, why after the first listener, instead of after the second one, below?

--
You received this message because you are subscribed to the Google Groups "blink-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-dev/26b02b28-4e5e-4276-974d-21e625ab0738%40chromium.org.

Leon

unread,
May 16, 2017, 5:43:08 AM5/16/17
to blink-dev, leon...@intel.com, chromi...@chromium.org, shi...@chromium.org, fal...@chromium.org
Sorry I should have clarified more context for my question.

Inside some event handlers(oninstall, onfetch etc.) of serviceworkerglobalscope [1], 
    onfetch = function(e) {
      e.waituntil(|some promise|);  
      --> ExtendableEvent::waituntil [2] is called in middle of event handler execution, this is OK.
    };
but
    onfetch = function(e) {
       Promise.resolve().then(() => e.waituntil(|some promise|));
      --> waituntil will be called by a microtask execution, this should NOT be allowed, while currently Chromium is allowing this.
    };

So, to implement the above restriction, I need to get the time point between the real event handler execution and microtask execution.

Jeremy Roman

unread,
May 16, 2017, 11:32:31 AM5/16/17
to Leon, blink-dev, Chromium-dev, Makoto Shimazu, Matt Falkenhagen
Hmm, interesting. Again, it's possible to have this case:

addEventListener('fetch', function() { ... });
addEventListener('fetch', function() { ... });

Where (unless I'm forgetting something about workers) will cause both listeners to be invoked, with the microtasks for the first before the listener function of the second. (I haven't checked, but this should be relatively easy to verify.)

Is there something preventing this? If not, presumably the second fetch event listener should be able to call ExtendableEvent::waitUntil as well.

If the problem is that waitUntil should be illegal during microtask execution, would v8::MicrotasksScope::IsRunningMicrotasks work?

Leon

unread,
May 17, 2017, 4:55:22 AM5/17/17
to blink-dev, leon...@intel.com, chromi...@chromium.org, shi...@chromium.org, fal...@chromium.org
Thank you very much! 
v8::MicrotasksScope::IsRunningMicrotasks is very helpful! 
And yes for the case of 2 fetch event listeners, the second listener should be able to call ExtendableEvent::waitUntil as well.

Another question about blink::ScriptPromise..
    Promise.resolve().then(() => { doSomething(); });
In C++ code , we can use ScriptPromise::Then to set {on_fulfilled, on_rejected} handlers against this ScriptPromise
but I found that on_fulfilled handler is always called back before doSomething() is called, 
then how could we get notified that doSomething() execution has just completed? I checked around ScriptPromise but did not find a way..

Jeremy Roman

unread,
May 17, 2017, 11:47:27 AM5/17/17
to Leon, blink-dev, Chromium-dev, Makoto Shimazu, Matt Falkenhagen
On Wed, May 17, 2017 at 4:54 AM, Leon <leon...@intel.com> wrote:
Thank you very much! 
v8::MicrotasksScope::IsRunningMicrotasks is very helpful! 
And yes for the case of 2 fetch event listeners, the second listener should be able to call ExtendableEvent::waitUntil as well.

Another question about blink::ScriptPromise..
    Promise.resolve().then(() => { doSomething(); });
In C++ code , we can use ScriptPromise::Then to set {on_fulfilled, on_rejected} handlers against this ScriptPromise
but I found that on_fulfilled handler is always called back before doSomething() is called, 
then how could we get notified that doSomething() execution has just completed? I checked around ScriptPromise but did not find a way..

You should wait for the total promise (returned from |then|) to be resolved. That promise won't be resolved until after doSomething completes (and in fact, will depend on whether doSomething throws an exception).
 

Makoto Shimazu

unread,
May 23, 2017, 12:31:37 AM5/23/17
to Jeremy Roman, Leon, blink-dev, Chromium-dev, Makoto Shimazu, Matt Falkenhagen
FWIW, let me share what I investigated around the microtasks execution as a note (though the problem looks already solved by IsRunningMicrotasks).

The timing of running the microtasks are implemented at here:
Microtasks run at the end of V8ScriptRunner::CallFunction, and CallFunction is called by V8WorkerGlobalScopeEventListener::CallListenerFunction.

We can probably catch the end of event handlers by tweaking around V8ScriptRunner or V8WorkerGlobalScopeEventListener, but it seems a bit awkward. 

Matt Falkenhagen

unread,
Mar 14, 2019, 9:26:09 PM3/14/19
to blink-dev, leon...@intel.com, chromi...@chromium.org, shi...@chromium.org, fal...@chromium.org
Let me resurrect this old thread...


On Tuesday, May 16, 2017 at 6:42:17 PM UTC+9, Leon wrote:
Sorry I should have clarified more context for my question.

Inside some event handlers(oninstall, onfetch etc.) of serviceworkerglobalscope [1], 
    onfetch = function(e) {
      e.waituntil(|some promise|);  
      --> ExtendableEvent::waituntil [2] is called in middle of event handler execution, this is OK.
    };
but
    onfetch = function(e) {
       Promise.resolve().then(() => e.waituntil(|some promise|));
      --> waituntil will be called by a microtask execution, this should NOT be allowed, while currently Chromium is allowing this.

It turned out this actually should be allowed. We want to allow it so long as the event's "dispatch flag" is set. Is the spec's dispatch flag equivalent to something in Blink, like Event::IsBeingDispatched()?
Reply all
Reply to author
Forward
0 new messages