JavascriptCore and event loop / timers

JavascriptCore doesn't come with any support for setTimeout (which is an API on the window/DOM object not provide din JavaScriptCore). So you need to implement a version of this yourself - no worries on that (there are examples to refer to out there, so it is fairly easy to set this up).

But I have come across an issue with this that I am not sure how to handle properly. It relates to when the timer callback fires and runs code in the JavaScript engine itself.

Consider this code snippet (assume I have provided an implementation of setTimeout):

console.log('Hello - here we go');

setTimeout(() => {
    console.log('Hi from setTimeout callback ...');
}, 0);

Promise.resolve().then(() => {
    console.log('Hi from promise');
});

console.log('Hi from main block');

In Node.js or say Safari, I would see this output:

Hello - here we go
Hi from main block
Hi from promise
Hi from setTimeout callback ...

So the promise then() is handled before the settimeout callback is handled. I think this is basically because Promise then() handlers are pushed onto something like a microtask queue, and the setttimeout callbacks on a separate queue, and the microtask queue is emptied before any other queue is processed (after completing the current event loop of course).

But when I implement this in JavaScript core, I don't always see the above - instead I can have:

Hello - here we go
Hi from main block
Hi from setTimeout callback ...
Hi from promise

So the timeout callback can be run BEFORE the promise handler. This obviously is different from Node or Safari.

Now I assume that is because the timeout callback is triggered from Swift native code that uses the call() API on a JSValue object that is provided when the settimeout is given to the native layer to process. And it seems that when native code attempts to execute JavaScript code (via call() or similar) then this is just executed as soon as possible - obviously not interrupting the Javascript core when executing any current event loop code, but potentially running between the point when the Javascript core finishes a normal event loop cycle and then starts processing the queued promise handlers.

This means that code that runs nicely in Node (for example) might not work the same way due to this behaviour.

Also, I also notice another thing: if JavaScript code makes a call to a native-provided method (e.g. by calling the setTimeout I show above, which I implement via a native-side handler) then during that call from JavaScript, it is possible for the native side to execute a call() and run Javascript code it wants. Again this is not what would happen in Node or Safari: it is not possible for timeouts (or network completions) to interrupt any 'builtin' function call, but in JavascriptCore it certainly is (to get around this I set a flag on the JavaScript side indicating a native call is being made, and if any native-triggered callback occurs on the javascript side when this flag is set, I have to 'queue' it via a promise handler for execution AFTER the current event loop is complete).

Are these known issues with Javascript core and are there ways to get around them?

thanks

JavascriptCore and event loop / timers
 
 
Q