Asynchronous Programming: A Reply

Lately, Jesse Warden printed a roughly Asynchronous Programming on DZone which may also be found on his own site. In fact, there are two or three misleading statements mostly due to the erroneous modification of the examples with JavaScript Promises and async/ /wait alterations.

An illustration of how JavaScript acts with Node.js is described as “.” The majority of this article applies to browser-hosted JavaScript as well.

Jesse’s initial pre-ECMAScript2015 conformant instance looks like this:

Quickly = () => console.log('1');
Slow = callback => 
  setTimeout(()=> 
    console.log('2');
    callback();
  , 1000);
;

rapid();
slow(()=> console.log('3'));
console.log('4');

If you are familiar with JavaScript, then you will not be astonished about the outcome of this code snippet being the figures 1 through 4 composed in the order 1, 4, 2, 3: the timeout event is simply handled after each of the synchronous code has resumed implementation.  

Later in his post, Jesse eliminates the call into setTimeout(), along with his code now looks like this:

Quickly = () => console.log('1');
Slow = callback => 
    console.log('2');
    callback();
;
rapid();
slow(() => console.log('3'));
console.log('4');

In this case, we get 1, 2, 3, 4. Jesse says: “Depending on ‘how fast’ your code operates, especially when unit testing using  mocks, at this point you have a race condition” This isn’t right. This code is totally synchronous and acts as it must – notably by implementing its statements. The telephone call into slow() is implemented immediately which means the two statements inside the lambda block are executed before the call into fast(). There’s no more race illness at all. Maybe his mocks introduced asynchronicity, which directed him to his decision.

This is Jesse’s variant of the case which uses promises:

Quickly = () => console.log('1');
Slow = () => fresh Promise(victory => 
    setTimeout(()=> 
        console.log('2');
        success();
    , 100);
);

rapid();
slow().then(()=> console.log('3'));
console.log('4');

In fact, what this code does is probably not to function as exactly what Jesse needs it to, namely, bringing code implementation in an arrangement which produces the output signal, 2, 3, 4. What it does not do would be to serialize the output signal. Truly, outputting 3 does not occur until the timeout event has happened and has been treated. Here is the behavior that’s implemented with this guarantee. On the other hand, the telephone call into slow() only generates the promise and initiates the activity by phoning the timeout() function without needing the timeout event to occur. So another actions is the call to afterward() which puts another callback onto the heap of activities to be implemented after the timeout event has been handled successfully. Both the event managing along with the actions stack are implemented only after all the synchronous code has completed performing – and perhaps some other stuff like the keyboard or another I/O has been processed. Hence, the outcome of the order 1, 2, 4, 3 is precisely what you should expect. Thus Jesse ends up with possible race conditions due to his improper use of claims.

Since you may have discovered, this variant does not require any parameters, therefore there is no way to pass a callback to it which is implemented while the timeout event occurs.

An – nearly – right implementation might appear like this:

Function fast() 
Console.log('1');


function slow(callback) 
  return new Promise((resolve(refuse) => 
    setTimeout(() => 
      console.log('2');
      callback();
      solve();
    , 1000);
  );


function doStuff() 
  fast();
  slow(() => console.log('3')).then(() => console.log('4'));


doStuff();

This really delivers the desirable string of 1, 2, 3, 4.

Here’s the same code when employing async/ /wait:

Function fast() 
Console.log('1');


function slow(callback) 
  return new Promise((resolve(refuse) => 
    setTimeout(() => 
      console.log('2');
      callback();
      solve();
    , 1000);
  );


async function doStuff() 
  fast();
  wait slow(() => console.log('3'));
  console.log('4');


doStuff();

I predict this code practically right, however, since it does not allow you to pass any parameters to the callback or to take care of possible return values of it. Here’s a much better version then is the one which also adds exception handling:

Function fast() 
Console.log('1');


function slow(callback, ...args) 
  return new Promise((resolve(refuse) => 
    setTimeout(() => 
      console.log('2');
      attempt 
        allow retVal = callback(...args);
        resolve(retVal);
       catch(e) 
        allow errObj = err: Cellular, args: [...args] ;
        refuse(errObj);
      
    , 1000);
  );


function doStuffWithPromises() 
  fast();
  slow((arg) => return arg; , '3').
   Subsequently((res) => console.log(res); ).
   Subsequently(() => console.log('4'); ).
   Subsequently(() => 
    fast();
    slow((arg) => throw('Oops!') ; , '3').
     Subsequently((res) => console.log(res); ).
     Subsequently(() => console.log('4'); ).
     Grab((e) => 
      console.log('Oh horror!') ;
      console.log('An error happened:'-RRB-;
      console.log('Error:', e.err);
      console.log('Arguments:', e.args);
    );
  );


async serve doStuffAsynchronously() 
  fast();
  var retVal = await slow((arg) => return arg; , '3');
  console.log(retVal);
  console.log('4');
  rapid();
  try 
    retVal = anticipate slow((arg) => throw('Oops!') ; , '3');
    console.log(retVal);
    console.log('4');
   catch(e) 
    console.log('Oh horror!') ;
    console.log('An error happened:'-RRB-;
    console.log('Error:', e.err);
    console.log('Arguments:', e.args);
  


doStuffWithPromises();
doStuffAsynchronously();

Since you may see, the return value of the initial callback reappears as input into the next callback passed into the first call of the afterward() system, in case you prefer working with claims. If not, this value is returned from the call to wait slow(), making it seem much like a cell phone call. Additional you may separate general (reusable) exception handling (which can be implemented here during the introduction of the promise, that is included in slow() itself) from the exception handling specific to the specific use scenario when slow() is predicted. This  nicely illustrates the principle you shouldn’t attempt to deal with exceptions entirely at a function when you don’t have enough info to take action in a meaningful manner. Only add a little bit of contextual information in case this makes sense, and rethrow. Somewhere further up at the call stack, exceptions could be handled as demanded by the needs of the company.

With wait, exception handling appears fairly natural and similar to that in synchronous code, along with debugging code is a lot easier in this case since you do not need to measure through a bunch of callbacks like you will need to when you utilize promises directly. This is the reason why I like working with wait.

As a summary, I’d like to state that when using guarantees properly, you can avoid race conditions altogether.