Monday 8 May 2023

How to prevent adding await/async to all functions?

Starting with a very simple example asking a user to confirm a choice:

let a = function(){ return window.confirm( "Proceed" ); };
let b = function(){ console.log("b: " + a() ); };
let c = function(){ b(); console.log( "after b" ); };
c();
c();

window.confirm() is an asynchronous event but the function behaves in a synchronous way and blocks execution until it has resolved.

Changing this to use a custom UI which uses await to block execution until the user responds. But the await documentation states:

The await operator is used to wait for a Promise. It can only be used inside an async function.

So the function containing the await expression has to be made async and returns a promise, not the value as before, and now a() does not block execution:

let a = async function(){
  let conf = document.createElement( "DIV" );
  conf.className = 'confirm';
  let message = document.createElement( "DIV" );
  message.className = 'confirm-message';
  message.innerText = 'Proceed';
  conf.appendChild( message );
  let ok = document.createElement( "BUTTON" );
  ok.innerText = 'Ok';
  conf.appendChild( ok );
  let cancel = document.createElement( "BUTTON" );
  cancel.innerText = 'Cancel';
  conf.appendChild( cancel );
  document.body.appendChild( conf );
  let v = await new Promise(
    response => {
      ok    .addEventListener( "click", ()=>{ response(true ); }, false );
      cancel.addEventListener( "click", ()=>{ response(false); }, false );
    }
  );
  conf.remove();
  return v;
};
let b = function(){ console.log("b: " + a() ); };
let c = function(){ b(); console.log( "after b" ); };
c();
c();
.confirm { display: grid; width: min-content; grid-template-columns: 10em 10em; grid-gap: 0.5em; grid-auto-rows: 1.4em; text-align: center; border: 1px #777 solid; background-color: #AAA; padding: 0.5em; }
.confirm > .confirm-message { grid-column: 1 / 3; }

This just moves the problem up the call stack and successive functions need to use await/async to ensure they wait until the user input has resolved:

let a = async function(){
  let conf = document.createElement( "DIV" );
  conf.className = 'confirm';
  let message = document.createElement( "DIV" );
  message.className = 'confirm-message';
  message.innerText = 'Proceed';
  conf.appendChild( message );
  let ok = document.createElement( "BUTTON" );
  ok.innerText = 'Ok';
  conf.appendChild( ok );
  let cancel = document.createElement( "BUTTON" );
  cancel.innerText = 'Cancel';
  conf.appendChild( cancel );
  document.body.appendChild( conf );
  let v = await new Promise(
    response => {
      ok    .addEventListener( "click", ()=>{ response(true ); }, false );
      cancel.addEventListener( "click", ()=>{ response(false); }, false );
    }
  );
  conf.remove();
  return v;
};
let b = async function(){ let v = await a(); console.log("b: " + v ); };
let c = async function(){ let v = await b(); console.log( "after b" ); };
(async function(){
  let c1 = await c();
  let c2 = await c();
})();
.confirm { display: grid; width: min-content; grid-template-columns: 10em 10em; grid-gap: 0.5em; grid-auto-rows: 1.4em; text-align: center; border: 1px #777 solid; background-color: #AAA; padding: 0.5em; }
.confirm > .confirm-message { grid-column: 1 / 3; }

Is there a way to use the new await syntax to block execution without changing the signature of all the calling functions and also making them async (to mimic the example using the built-in window.confirm() function)?



from How to prevent adding await/async to all functions?

No comments:

Post a Comment