JavaScript async/await in APEX

JavaScript async/await in APEX

The following technique will change the way we write JavaScript very soon.

If you're into JavaScript, you're gonna want to understand async/await.

Prerequisites

JavaScript Promises were introduced to solve many JavaScript problems such as the callback hell. async/await builds on top of Promises to provide an easier handling of asyncronous functions.

You may understand the examples in this blog post, but here's a little background on synchronicity vs asynchronicity and JavaScript Promises: GHOST_URL/javascript-promises-in-apex/

Async

async is simply a keyword you put in front of a function definition. Example:

async function doSomething() {
   // do something asyncronous
   return 'something';
}

Whenever you invoke doSomething(), it will be treated asynchronously. An async function always returns a Promise. That promise will contain the value that you defined (something in the case above). More on this later.

If you look at the following:

function start() {
    console.log('start');
    console.log(doSomething());
    console.log('end');
}

start();

You might expect to get

start
something
end

But in reality you will get

start
end
something

That's because doSomething() was launched asynchronously.

That annoys a lot of people, and it leads to some very ugly code to and it becomes a pain to maintain all your calls in sequential order.

But remember, asynchronous JavaScript is better for the performance of your code, so we have to stick with it.

Await

This is where the magic happens. When using await, you're essentially telling your code to stop and wait for the result of the asynchronous function to complete before going any further.

await pauses until the async function is done. By doing so, it allows you to write a natural top to bottom code without relying on callbacks or Promises to simulate a synchronous process.

await always expects a Promise from the async function, and parses the result automatically.

Let's try the same example again:

async function doSomething() {
   // do something asyncronous
   return 'something';
}

async function start() {
    console.log('start');
    console.log(await doSomething());
    console.log('end');
}

start();

Now, the result will be:

start
something
end

And that is because we're awaiting the function doSomething to complete before continuing.

Note: await can only be used within an async function.

Usage in APEX

Here's a demo application so you can run the demos yourself.

In the examples below, I am using an AJAX process called hang which loops long enough to hang the process for X seconds (with a given parameter x01). PL/SQL code is not relevant here.

Here's the JavaScript function that I will be using to call the AJAX process:

async function ajaxHang(seconds) {
    return apex.server.process(
        "hang", {
            x01: seconds
        }
    );
}

Notice how I am NOT handling the success callback or the error handling here. More on this later.

Example 1)

This example invokes one AJAX process using async/await

async function example1() {    
    // invokes ajaxHang(1) which takes 1 second until it's done (PAUSE)
    var result = await ajaxHang(1);

    // prints the result to the region
    console.log('example1', result);
}

Here we have waited a total of 1 second:

  • 1 seconds for ajaxHang(1)

Example 2

This example invokes two AJAX processes consecutively using async/await

async function example2() {    
    // invokes ajaxHang(1) which takes 1 seconds until it's done (PAUSE)
    var result1 = await ajaxHang(1);

    // prints the result to the region
    console.log('example2', result1);
    
    // invokes ajaxHang(2) which takes 2 seconds until it's done (PAUSE)
    var result2 = await ajaxHang(2);
    
    // prints the result to the region
    console.log('example2', result2);
}

Here we have waited a total of 3 seconds for the sum of all AJAX calls

  • 1 second for ajaxHang(1)
  • 2 seconds for ajaxang(2)

Example 3

This example invokes two AJAX processes simultaneously using async/await

async function example3() {    
    // invokes result 1 and result 2 at the same time
    var result1 = ajaxHang(1);
    var result2 = ajaxHang(2);

    // The results are printed in order as the AJAX calls return
    console.log('example3', await result1);
    console.log('example3', await result2);
}

Here we have waited a total of 2 seconds because ajaxHang(2) took the longest to complete

  • 1 second for ajaxHang(1)
  • 2 seconds for ajaxHang(2)

Example 4

This example invokes X number of AJAX processes using async/await & Promise.all feature

async function example4() {    
    // invokes result1, result2 and result3 at the same time
    var result1 = ajaxHang(1);
    var result2 = ajaxHang(2);
    var result3 = ajaxHang(3);

    var results = await Promise.all([result1, result2, result3]);

    // The results are printed all at once, when all AJAX calls have returned
    console.log('example4', results);
}

Here we have waited a total of 3 seconds because of ajaxHang(3) took the longest to complete:

  • 1 second for ajaxHang(1)
  • 2 seconds for ajaxHang(2)
  • 3 seconds for ajaxHang(3)

Error handling

Surprisingly, the correct way to handle errors within async/await is by using a good old try/catch. The example from above could become:

async function doSomething() {
   // do something asyncronous
   return 'something';
}

async function start() {
    try {
        console.log('start');
        console.log(await doSomething());
        console.log('end');
    } catch(e) {
        console.error(e);
    } finally {
        console.log('Do something no matter what');
    }
}

start();

Browser Support

Async/await in supported in all major browsers today, but it will most likely never be supported in IE.

Screenshot_1

Keep this in mind if you decide to use it.

It is also available from Node.js 7.


I am very excited about this. I think it's fabulous that we can finally write simple top to bottom JavaScript while benefiting from the asynchronous behavior.

I was initially inspired by Dan McGhan's post on async for Node.js. Good stuff!

Thoughts?