Defer expression

# Karl Thunberg (7 years ago)

is there any proposal for an expression to invoke something after the current call stack has cleared? Lodash defer is a good example.

Just like I can use await to pause the execution of the current call, it would be nice to also have an expression to queue up something that cannot run immediately e.g. defer doThisLater();

The current solution is to use setTimeout with a 1 ms delay but that is not a good solution as it is not the same thing and it makes it more difficult for people to understand what is the true purpose of that line of code.

# Darien Valentine (7 years ago)

Deferring can mean different things, but generally then covers this well (specifically, pushing execution of something to the end of the current task).

Promise.resolve().then(stuffToDefer);

I think there is a proposal for Promise.try(stuffToDefer), which would eliminate the need for the resolve bit.

# Logan Smyth (7 years ago)

Promise.try calls its callback synchronously like new Promise does, so it would not be a substitute for this case.

# T.J. Crowder (7 years ago)

I believe this is the most recent on this topic: esdiscuss.org/topic/microtask-scheduling

-- T.J. Crowder

# Darien Valentine (7 years ago)

@logan ah, oops, that was an (incorrect) assumption about the proposed behavior on my part

# Matthew Robb (7 years ago)

I think this will actually get you what you're after:

(async function () {

await null; // Deferred code here

})()

On Aug 16, 2017 5:46 PM, "Darien Valentine" <valentinium at gmail.com> wrote:

@logan ah, oops, that was an (incorrect) assumption about the proposed behavior on my part

# kai zhu (7 years ago)

setTimeout is still the best solution in my mind. none of the promise or async code examples presented are as immediately obvious as setTimeout that the code is to self-run at a later time (and you don't need the 1ms argument).

// self-comment that this code will self-run
// after the main script in a reasonably immediate fashion
setTimeout(function () {
    // deferred code
})
# Naveen Chawla (7 years ago)

An in built Promise version of setTimeout would be cool: Promise.delay() and Promise.delay(500)

# Logan Smyth (7 years ago)

setTimeout it is defined in the HTML spec, www.w3.org/TR/html5/single-page.html#timers, not the ECMA spec. The ECMA spec has no concept of time-based delays at all, promise-based or otherwise.

# Matthew Robb (7 years ago)

Honestly have there been any proposals for something like do async { // can await here } which would produce a promise in the enclosing scope

  • Matthew Robb
# Tab Atkins Jr. (7 years ago)

On Thu, Aug 17, 2017 at 12:12 PM, Matthew Robb <matthewwrobb at gmail.com> wrote:

Honestly have there been any proposals for something like do async { // can await here } which would produce a promise in the enclosing scope

Do-expressions haven't advanced in general yet, but if/when they do, this seems like it might be reasonable. It's just sugar for the Promise.resolve().then(()=>{...}) expression, right?

# Matthew Robb (7 years ago)

Yeah essentially although I'd think of it more as sugar for:

(async () => { await null; ... })()

# kai zhu (7 years ago)

i can give you a specific solution to one of my async init problems (done entirely in vanilla es5 code). in this use-case, i want to run integrated tests, but have to wait for the server to listen on a port, and the db to be seeded first. to keep complexity at a manageable level, i find its worth the tradeoff to pollute the global namespace with initialization functions and counters, especially if you intend to break up the below example into separate modules.

global.initializationCounter = 0;

global.initializationCounterDecrement = function () {
/*
 * this function will decrement the initialization counter,
 * and if the initialization counter reaches zero, then run the post-init code
 */
    global.initializationCounter -= 1;
    if (global.initializationCounter === 0) {
        global.postInit();
    }
};

global.postInit = function () {
/*
 * run your custom post-init code here
 */
    // run test-runner after initialization
    global.testRun();
};

...

var db, http, server;
http = require('http');

// pre-init1 - create http server,
// and wait for server to listen to port 8080, before running post-init code
server = http.createServer(function (request, response) {
    // request handler
    ...
});
global.initializationCounter += 1;
server.listen(8080, global.initializationCounterDecrement);

// pre-init2 - create db,
// and wait for db to seed, before running post-init code
db = ...;
global.initializationCounter += 1;
db.insert(<data1>, global.initializationCounterDecrement);

global.initializationCounter += 1;
db.insert(<data2>, global.initializationCounterDecrement);

// pre-init3 - any extra async init code
global.initializationCounter += 1;
setTimeout(function () {
    // you custom code
    ...
    global.initializationCounter();
});
# Isiah Meadows (7 years ago)

Closer to the proposed Promise.try, in that it starts synchronously.

Also, normal do expressions are stage 1 currently, and the core concept hasn't changed much in the several years of the proposal's existence. (It's based on statement completion values, like eval.)

# dante federici (7 years ago)

kai zhu has a good example of solving async order problems explicitly, as opposed to banking on any defer or timeout to figure it out.

I'm a little curious of what you mean by "something that cannot run immediately".

If it can't be run immediately, then there should be something you can hook into to run -- otherwise you're playing guesswork on what is currently scheduled to execute.

Examples of the arbitrary use of "setTimeout" to defer to another script frame in the DOM is when a frontend framework hasn't created elements yet, and you need to run a query selector to grab them, or similar shenanigans.

I'd argue more that these are all anti patterns, since why should you be guaranteed that toDoLater() can even be run after the setTimeout(1) or when the Promise scheduler runs?