Asynchronous output

Javascript and Node.JS make heavy use of asynchronous execution. IJavascript lets you exercise these asynchronous capabilities, both:

  • by updating stdout and stderr asynchronously, or
  • by providing an execution result asynchronously.

Synchronous vs asynchronous code

In simple words, this tutorial use the term "asynchronous code" to refer to code that doesn't run immediately (code in callback functions). For example, this is asynchronous code:

In [1]:
function sayHello() {
    console.log("Hello, World!");
}
var t = setTimeout(sayHello, 1000);
Hello, World!

whereas this is not:

In [2]:
function sayHello() {
    console.log("Hello, World!");
}
sayHello();
Hello, World!

$$.async()

By default, IJavascript assumes an execution request is synchronous code. That is, IJavascript will evaluate the execution request, return the result and advertise that is ready to receive more requests.

If an execution request invokes the function $$.async(), IJavascript will treat the execution request as asynchronous code. Unlike synchronous code, asynchronous code should return a result by invoking $$.sendResult(result) or $$.sendError(error). Or alternatively, invoke $$.done() to indicate that no result will be returned. For example:

In [3]:
$$.async();

console.log("Hello, World!");

setTimeout($$.done, 1000);
Hello, World!

Accessing the global object $$ from asynchronous code

Asynchronous code should make a copy of the global object $$, to ensure a result is returned to the appropriate cell. There are several patterns to make this copy, each with their own pros and cons:

  • using a variable to store a copy the $$ linked to the asynchronous cell,

  • using a function environment,

  • or using a block-scoped let (requires ES6).

Using a variable to store a copy of the $$ linked to an asynchronous cell

The main drawback of this pattern is that it requires a new variable per asynchronous cell. See example:

In [4]:
$$.async();

var cell1 = {
    $$: $$,
    console: console,
};

setTimeout(function() {
    cell1.console.log("Hello, world!");
    cell1.$$.sendResult("Bye!");
}, 1000);
Hello, world!
Out[4]:
'Bye!'

Using a function environment

A function environment lets you create a copy of a global variable into a local variable with the same name (and thus, it doesn't require the creation of a new variable name for every asynchronous cell). See the example below:

In [5]:
$$.async();

(function($$, console) {
    setTimeout(function() {
        console.log("Hello, world!");
        $$.sendResult("Bye!");
    }, 1000);
})($$, console);
Hello, world!
Out[5]:
'Bye!'

Using a block-scoped let (requires ES6)

If the version of Node.js in your system supports ES6, you can use let to define local variables in a block scope that contains the asynchronous code:

In [6]:
"use strict";

$$.async();

{
    let console = global.console;
    let $$ = global.$$;

    setTimeout(function() {
        console.log("Hello, world!");
        $$.sendResult("Bye!");
    }, 1000);
}
Hello, world!
Out[6]:
'Bye!'