Proposal: optional await ej: await(true/false)

hi, it always happens that many times I have to write the code twice to tell a function if it is asynchronous or not, since sometimes it is not necessary to wait for a response.
the code ends up looking like this:

if(isAsync){
   let myList = await model.getList();
} else {
   let myList = model.getList();
};

OR

let myList = isAsync ? await model.getList() : model.getList();

PROPOSAL
that is why I propose the following syntax sugar: pass optional boolean in await to set async call.
thus reducing the code and making life a little easier

let myList = await(isAsync) model.getList();

if is Async = false, the await word will be ignored.
we could activate and deactivate await dynamically

Usually, you can just write:

let myList = await model.getList();

That will work also for sync functions; it will just unconditionally wait a turn before proceeding to the next instruction.

3 Likes

https://blog.izs.me/2013/08/designing-apis-for-asynchrony/ is also a useful article to read if you are designing an API, to avoid making one that's sometimes sync and sometimes async.

1 Like

Sure, but this new parameter will allow you to continue with the code dynamically, so whenever you want you could turn off the await

why would that be a good thing tho? That means the timing of the code would be unpredictable. The linked article I provided discusses this thoroughly.

1 Like

for example when you have dynamic services which can work with global memories and at the same time with databases. For speed you use global memory to access the data but when the data is too much, you start using the services. At the time you were using global memories, you don't care about awaits since you access and work on those variables, but the transactions must still be executed in the database. This allows you to speed up reading and writing in systems with few users but high concurrency. as a problem it appears that you must have conditionals for each case and configuration (), literally duplicating your code.

referring me to backend in NODEjs. especially from custom software that requires a lot of optimization and configuration for BIG DATA

@JhonnattanRivera I thought await was optimised well enough when dealing with non-promises, and the supposed speedup when avoiding it largely a myth. Can you share your benchmarks where this is not the case?

1 Like

ok, but give me a few hours to leave my office and send you a clear example

@JhonnattanRivera Beware that the difference of syncFoo(); and await syncFoo(); is more than just waiting for the next tick; because interesting things may happen between now and the next tick. You are at risk of releasing Zalgo, which means concretely, in your example, having unrelated race conditions and heisenbugs that manifest themselves only when you read from the database instead of the memory, or vice versa, or worse (e.g. with specific interleaved memory readings and database readings).

That said, if you are really sure to release Zalgo, you are not obliged to duplicate the code, only the variable names:

// only the variable name “myList” appears more than once
let myList = model.getList();
if (isAsync)
    myList = await myList;

This is still lengthy, but I hope that I won’t find a shorter (yet readable) pattern, otherwise I would be tempted to use it.

3 Likes

You don't have to check whether your code is asynchronous or not, using await on a synchronous code doesn't cause a bug.

In fact, I have seen synchronous looking code, that did not work until I made the function async and used await on a previous line of code for the next line to work.

To be specific:
Line 1: element.insertAdjacentHTML('where', an html with children of same class)

On Line 2, I want to loop through all those classes, but it turns out that that, the control flow already got to line 2 before the code in line 1 finishes.

So I made the function block containing these two async and I used await on line 1, and cool the problem solved.

1 Like

if you do an await on a non-promise, you still get the result.

const unchangedData = await syncCode()

not sure I understand why this would be useful.

1 Like

I've run into this awkwardness before, and I find it way easier to just implement a generator for this that handles await behind the scenes as needed. In fact, I did just that to optimize a code path via common logic while still being able to support both sync and async invocation.

Edit: I don't believe this use case is common enough to warrant any syntax additions.

2 Likes

How do you turn on/off await? The only way to use await is inside an async function(){} or async ()=>{}, so there's no way to know ahead-of-time whether to make your code async or not if you are going to be using await. Here's a demonstrification of how await works under the hood:

async await_demonstrification(value) {
    if (typeof value === "object" && "then" in value && typeof value.then === "function") {
        return await value;
    } else {
        return value;
    }
}

The only way I can think of to switch between async and sync is to write the code twice, which is less than ideal and something that your proposal does not solve:

// Less maintainable dual sync/async version:
function maybeAsync(model)  {
    if (model instanceof Promise) {
        return async function() {
            return await (await model).getList(); // async
        };
    } else {
        return model.getList(); // sync
    }
};

// More maintainable sync/async version:
async function alwaysAsync() {
    return await (await model).getList(); // async
};

Lastly, if you really do need to use await this way, there's already a pipeline spec which is underway and will fulfill the same purposes as you desire:

let myList = model.getList() |> isAsync ? await # : #;

Or, you could roll out your own piping system:

var X, PIPE = function(value) {X = value; return true}; // setup code
//...
let myList = PIPE(model.getList()) && isAsync ? await X : X;
// OR:
PIPE(model.getList()); let myList = isAsync ? await X : X;

If your reasoning is performance, then you need to rethink your approach. Promises will always have terrible performance and should only be used for the larger macro tasks, never the tiny micro tasks. For the micro tasks, always use callbacks, otherwise you will be in a world of hurt when it comes to performance.

I think you have a good proposal that may help some people, but it is much too specific and will be used so infrequently that's its not work the complexity it would add to both the language and the browsers.