Conference PaperPDF Available

A JavaScript Transpiler for Escaping from Complicated Usage of Cloud Services and APIs


Abstract and Figures

We propose Escapin, a JavaScript transpiler for developing application programs that consume APIs and are deployed on cloud services in order to obtain new business concepts by trial-and-error iterations. Escapin tackles two difficulties in consuming API and using cloud services; 1) understanding the correct usage of cloud services and APIs requires a lot of time, and 2) programs using cloud service SDKs and HTTP clients are complicated. Escapin introduces two new features to the ECMAScript 2018 specification for simplifying application programs: importing APIs as objects and exporting objects and functions as cloud service resources. Escapin transpiles a JavaScript program independent from any platform into a JavaScript program containing concrete usages of APIs and cloud services. Escapin encourages engineers to write a callback-agnostic program, which represents asynchronous processing as a completely synchronized procedure without any asynchronization feature such as callback, Promise, async/await, etc. Escapin also accepts programs having nesting callbacks. Escapin destructures nesting callbacks in the programs to obtain a callback-agnostic program. Our experimental results show that in the task of developing an application that consumes Uber API and is deployed on Amazon Web Services, Escapin statistically significantly reduces development time and the metrics regarding the simplicity of source code with huge effects: lines of code, cyclomatic complexity, and cognitive complexity.
Content may be subject to copyright.
A JavaScript Transpiler for Escaping from Complicated Usage
of Cloud Services and APIs
Kosaku Kimura, Atsuji Sekiguchi, Shridhar Choudhary and Tadahiro Uehara
Fujitsu Laboratories Ltd. {kimura.kosaku,sekia,s.choudhary,uehara.tadahiro}
Abstract—We propose Escapin, a JavaScript transpiler for
developing application programs that consume APIs and are
deployed on cloud services in order to obtain new business
concepts by trial-and-error iterations. Escapin tackles two
difficulties in consuming API and using cloud services; 1)
understanding the correct usage of cloud services and APIs
requires a lot of time, and 2) programs using cloud service
SDKs and HTTP clients are complicated. Escapin introduces
two new features to the ECMAScript 2018 specification
for simplifying application programs: importing APIs as
objects and exporting objects and functions as cloud service
resources. Escapin transpiles a JavaScript program inde-
pendent from any platform into a JavaScript program con-
taining concrete usages of APIs and cloud services. Escapin
encourages engineers to write a callback-agnostic program,
which represents asynchronous processing as a completely
synchronized procedure without any asynchronization fea-
ture such as callback, Promise, async/await, etc. Escapin
also accepts programs having nesting callbacks. Escapin
destructures nesting callbacks in the programs to obtain a
callback-agnostic program. Our experimental results show
that in the task of developing an application that consumes
Uber API and is deployed on Amazon Web Services, Escapin
statistically significantly reduces development time and the
metrics regarding the simplicity of source code with huge
effects: lines of code, cyclomatic complexity, and cognitive
Index Terms—Transpiler, Source-to-source Compiler, Meta-
programming, Cloud Computing, Web APIs
1. Introduction
Software engineers should undoubtedly stand on the
shoulders of giants. A lot of cloud services and public Ap-
plication Programming Interfaces (APIs) are available for
developing applications [1]–[4]. Cloud services enable on-
demand provisioning of configurable computing resources
(e.g., servers, data store, networks, and so on) via network
access to services [5], and public APIs provide various
kind of resources: not only data, functions, things [6] but
also humans [7], [8]. Cloud services and APIs are fully
managed by their providers in order to guarantee their
service levels. We can just use them by Software De-
velopment Kit (SDK) or Command-Line Interface (CLI)
without efforts to maintain them.
The easier we can utilize various emerging technolo-
gies, the faster the speed of business change is getting.
According to the Lean Startup [9] methodology, compa-
nies should launch and update applications more quickly
in order to find business concepts that can obtain real
customers. The use of cloud services and APIs really
makes sense in that context, because the engineers do not
need to take their own time and much money for preparing
and maintaining resources.
1import api from "";
2import { resize } from "imagemagick";
4export const images = {};
6export function uploadImage(event) {
7const {name, image} = event.formData;
8const buffer = Buffer.from(image, "binary");
9 images[name] = buffer;
10 try {
11 const {stdout, stderr} = resize({
12 srcData: buffer,
13 format: "jpeg",
14 width: 100
15 });
16 const data = Buffer.from(stdout, "binary");
17 api.thumbnails[name] = {name, data};
18 return {message: "Succeeded"};
19 } catch (err) {
20 throw err;
21 }
22 }
Figure 1. A JavaScript program to be transpiled by Escapin to create a
cloud application.
JavaScript is one of the most suitable programming
languages for creating applications using cloud services
and APIs. It is runnable both on the web browser and on
a server using Node.js [10] runtime and also supported
by most of the major clouds. However, there are two ma-
jor difficulties in developing applications by using cloud
services and APIs.
1) Understanding the correct usage of cloud services
and APIs requires a lot of time: because there are too
many configuration parameters and types of resources, we
need to check documents of SDKs or CLIs many times
during development time. It generally takes a lot of time.
Undocumented knowledge, inaccurate documentation and
lack of examples are making it worse [11].
2) Programs using cloud service SDKs and HTTP
clients are complicated: programs that use several cloud
services and APIs tend to be complicated and hard to
understand due to containing asynchronous processing
[12]. It makes the programs difficult to be maintained and
enhanced in the future.
There are several previous works (e.g., [13]–[19])
tackling those difficulties. However, those works need
additional dependencies (modules, proprietary program-
ming models, etc.), and application programs using them
would be still complicated. In order to follow the speed
of business change, we consider the application programs
should be simplified in order to easily grasp those business
logics even if we have to use cloud services and APIs to
implement them.
In order to address these difficulties, we propose Es-
capin, a JavaScript transpiler for developing application
programs that consume APIs and are deployed on cloud
services. Figure 1 shows a JavaScript program to be
compiled by Escapin. We can write an application that
is deployed on cloud services and consumes an API, by
using a simple notation provided by Escapin. The program
does not need cloud service SDKs and HTTP clients and
represents what to do as a simple procedure.
The main contributions of the paper are:
1) In order to reduce the complexity of cloud service
usage and API consumptions, we introduce two
new features to the ECMAScript 2018 specifi-
cation for simplifying application programs: im-
porting APIs as objects and exporting objects and
functions as cloud service resources.
2) In order to improve the source code simplic-
ity, along with the above features, we provide
callback-agnostic programming, which represents
asynchronous processing as a completely syn-
chronized procedure without any asynchroniza-
tion feature such as callback, Promise, and also
async/await. Escapin also accepts a program
having nesting callbacks. Escapin destructures
nesting callbacks in the programs to obtain a
callback-agnostic program.
3) We conducted the experimental study to inves-
tigate the effectiveness of our approach and the
simplicity of callback-agnostic programs, by de-
signing a task of developing an application that
consumes Uber API [20] and is deployed on
Amazon Web Services (AWS) [1]. The results
show Escapin statistically significantly reduces
development time and the metrics regarding the
simplicity of source code with huge effects: lines
of code, cyclomatic complexity [21], and cogni-
tive complexity [22].
The remainder of the paper is organized as follows. In
Section 2, we describe an example that motivates our re-
search. In Section 3, we describe the overview of Escapin.
In Section 4, we describe the transpilation procedure in
Escapin. In Section 5, we show experimental results to
investigate effectiveness and source code simplicity of
Escapin. In Section 6, we show our related work, and
we conclude the paper in Section 7.
2. Motivating Example
Suppose to create a JavaScript program shown in
Fig. 2. The program consists of a function which
puts an image data into an AWS S3 bucket, resizes
data as a thumbnail by imagemagick, and sends
the thumbnail image to an API by using HTTP client
request-promise-native. The program is to be
deployed to AWS Lambda [23].
There exist three features to implement asynchronous
processing in Fig. 2.
Error-first callback: an old-school way to get a value
asynchronously. Error-first means a function whose first
parameter is assigned null if process has succeeded
or an error object if failed. The first parameter is
generally named as to be recognized as an error ob-
ject, e.g., error,err,ex,e, etc. There is an error-
1const { resize } = require("imagemagick");
2const rp = require("request-promise-native");
3const AWS = require("aws-sdk");
5 exports.uploadImage
6 = async (event,context,callback)=>{
7const {name, image} = event.formData;
8const buffer = Buffer.from(image, "binary");
9await new AWS.S3().putObject({
10 Bucket: "images_foobar",
11 Key: name,
12 Body: buffer
13 }).promise();
14 resize({
15 srcData: buffer,
16 format: "jpeg",
17 width: 100
18 }, (err, stdout, stderr) => {
19 if (err) {
20 callback(err);
21 return;
22 }
23 const data = Buffer.from(stdout,"binary");
24 rp({
25 url:‘${name}‘,
26 method: "put",
27 formData: {name, data},
28 headers: {
29 "content-type": "multipart/form-data"
30 }
31 }).then(() => {
32 callback(null, {message: "Succeeded"});
33 }).catch(err => {
34 throw err;
35 });
36 });
37 }
Figure 2. A JavaScript program to be deployed to AWS.
first callback function (err, stdout, stderr) =>
{...}in the parameters of function resize. The func-
tions of imagemagick require such callback function
to get a processed image by stdout and some error
message by err or stderr if some error has occurred.
Promise: a feature supported by ECMAScript 6
(ES2015) or later. Promise is a proxy of a value
that will be assigned in the future [24]. There are
two functions which return Promise in Fig. 2: new
AWS.S3().putObject({...}).promise() and
rp({...});Promise requires a callback function
(resolve, reject) => {...}, where resolve
is a callback function to return a successful result
and reject is a callback function to return an error
information. We can use functions then and catch
provided by Promise values to get resolved and rejected
values, respectively.
async/await: a feature supported by ECMAScript 2017
or later. Keyword async makes functions as that they al-
ways return Promise values by automatically wrapping
returned values in Promise and exceptions into rejected
ones. Keyword await can be put on a Promise in
async functions when the Promise should be resolved
before the next statement. Although async/await is the
best way to write asynchronous processing in JavaScript
so far, it is a bit hard to use them correctly. We have to
still keep in mind which functions return Promise and
what we manipulate in the program is Promise. What
is worse is that, because the author of the program of Fig.
2 fails to use async/await to all possible places, it is
Method Path Header Body Example
GET /items api.items
GET /items/{id}api.items[id]
GET /items/{id}/props/{pid}api.items[id].props[pid]
GET /items/{id}?foo=true api.items[id][{foo: true}]
GET /items/{id}?foo=true x-bar: abc api.items[id][{foo: true, x-bar: "abc"}]
POST /items {baz: 42}api.items({baz: 42})
PUT /items/{id}x-bar: abc {baz: 42}api.items[id][{x-bar: "abc"}] = {baz: 42}
DELETE /items/{id}delete api.items[id]
getting harder to understand how it works.
In order to create a program such as Fig. 2, we have to
learn how to use AWS SDK, HTTP clients and the above
JavaScript features for asynchronous processing. Although
what we want to realize in the program is quite simple,
we have to experience a time-consuming and error-prone
programming journey.
3. Proposed approach: Escapin
Escapin is based on the source-to-source compiling
technology also known as transpiling or codemod. In
this paper, we use the Babel [25] ecosystem as the
basic transpiling functionalities. Babel has a JavaScript
parser, babylon1. By using babylon, Escapin parses
a JavaScript program into Abstract Syntax Tree (AST)
conforming to the ESTree2specification, which is a de-
facto standard of JavaScript AST specification, and re-
places AST nodes with the refined ones.
Escapin introduces two additional features for sim-
plifying API consumptions and cloud service usage.
Those features neither modify nor break the existing EC-
MAScript specification. Along with the features, Escapin
provides callback-agnostic programming.
3.1. Importing APIs as objects
The first feature is to import APIs as objects. Escapin
borrows the syntax of import declarations from the
ES2015 specification. Figure 3 shows an example usage of
importing APIs. Line 1 in Fig. 3 is an import declaration
for an API defined by Open API Specification (OAS) 2.0.
We can import the API by giving a valid URI of the API
specification file. Escapin uses swagger-parser3to
parse OAS 2.0 files. If the file downloaded from the URI
is successfully parsed by swagger-parser, Escapin
recognizes the identifier api as an API variable. Other-
wise, Escapin recognizes the import declaration as a usual
module import.
Table 1 shows the API manipulation examples. We can
represent an API access (i.e., sending a request, and then,
waiting for receiving a response) as a simple manipulation
of the API variable. Suppose that the API specification
is defined as shown in Fig. 4, there is a resource path
/items/{id}accessible with method GET. This means
the API has a collection resource of items such that
each item has a given id. In this case, variable api has
property items, and items also has a property with
a computed name. The HTTP response of request GET
1import api from "http://path/to/swagger.yaml";
3 item = api.items[id];
Figure 3. Importing API defined by OpenAPI Specification.
1 swagger: "2.0"
2 info:
3 title: "Awesome API"
4 description: "An awesome API"
5 version: "1.0.0"
6 host: ""
7 schemes:
8 - "https"
9 basePath: "/v1"
10 produces:
11 - "application/json"
12 consumes:
13 - "application/json"
14 paths:
15 /items/{id}:
16 get:
17 parameters:
18 - name: "id"
19 in: "path"
20 type: "string"
21 required: true
Figure 4. API spec. file at http://path/to/swagger.yaml.
/items/{id}can be written as api.items[id] (cf.
Line 3 of Fig. 3).
This notation idea is originally inspired by the practi-
cal interpretation [26] of REpresentational State Transfer
(REST) [27]. We simply use plural nouns as resource
path names and HTTP methods as verbs for representing
manipulation of resources. Using plural nouns is suitable
to represent two types of resources: single item and collec-
tion. A resource path that ends with a unique id as a path
parameter (e.g., /items/{id}) represents an item with
the id. We consider the major HTTP methods GET,POST,
PUT and DELETE can be loosely mapped into CRUD
From the following intuitions, we consider the notation
has advantages against notations using existing HTTP
RESTful APIs that manipulate data with method
GET,PUT, and DELETE look just like manipu-
lations of objects. In most cases, resource hierar-
chies of the RESTful APIs simply correspond to
the data structures provided by them.
Non-RESTful APIs, which contain verbs in re-
source paths and often use method POST, look
just like function calls. For example, we can write
POST /uploadImage as uploadImage().
1export const items = {};
3export function handle(req) {
4const {name, item} = req.body;
6 items[name] = item;
8try {
9return doSomething(item);
10 } catch (err) {
11 throw err;
12 }
13 }
Figure 5. Exporting an object and a function as a data store and a
computing resource, respectively.
3.2. Exporting objects and functions as cloud
service resources
The second feature is to export objects and functions
as concrete cloud service resources. Escapin borrows the
syntax of export declaration from the ES2015 specifi-
cation. Figure 5 shows the example usage of exporting an
object and a function. An exported object corresponds to
a resource on storage services for key-value pairs, and an
exported function corresponds to a resource on Function
as a Service (FaaS).
The application developed by Escapin can also be
an API server by deploying exported functions as API
handlers. In API specification file, there is an extension pa-
rameter for mapping exported functions to a combination
of an HTTP method and a path: x-esc-handler to be
assigned <module name>.<exported function
name> as the handler location. The function can get
query parameters, path parameters, headers and message
body from the properties of the first argument. Those
property names are the same as field in of each parameter
defined by API specification; the value of field in can be
either query,path,header,body or formData4.
For example, we can get parameter name in the message
body by, where the first argument is
req (cf. Line 4 in Fig. 5).
There are the following intuitions in the notation:
Storage services for key-value pairs look just like
FaaS resources are just identical to exported func-
3.3. Callback-agnostic programming
Escapin provides the callback-agnostic programming,
which represents asynchronous processing as a completely
synchronized procedure without any asynchronization fea-
ture such as callback, Promise, and also async/await.
Regarding functions that return Promise, we can sim-
ply forget where async and await should be placed.
Regarding functions that require error-first callback func-
tions, for each function call, we can remove its callback
and get parameter values that are originally given to
the callback as return values of the function call. The
first parameter, i.e., error information, is thrown as an
exception, and we can catch it by wrapping the function
call in a try-catch statement.
APIsandClouds Asynchronization
Figure 6. Overview of transpilation procedure in Escapin.
1 func(arg,
2 (err,p1,p2)=>{
3if (err) {
4 handleError(err);
6 }
7 doSomething(p1,p2);
8 });
1try {
2const {p1,p2}
3 = func(arg);
4 doSomething(p1,p2);
5 } catch (err) {
6 handleError(err);
7 }
Figure 7. Destructuring a callback.
4. Transpilation
This section describes the transpilation procedure of
Escapin. Figure 6 shows the procedural overview of the
transpilation. Transpilation consists of three steps: 1) de-
structuring nesting callbacks, 2) conducting refinements
for APIs and platforms, and 3) asynchronizing function
calls in accordance with their function types determined
beforehand. Escapin transforms a JavaScript program that
just describes what to do without any platform-specific
configuration, into a JavaScript program that contains con-
figurations and manipulating resources of cloud services.
We call the former a platform-independent program (PIP)
and the latter a platform-specific program (PSP).
There are two types of inputs of Escapin: PIPs and
configuration files. We do not need to modify PIPs if we
just want to change requirements to the target platform
without changing application logic.
4.1. Destructuring nesting callbacks
As well as PIPs created by conforming to callback-
agnostic programming, Escapin also supports legacy pro-
grams that contain nesting callbacks required for using
some legacy module such that assigning an error-first call-
back function is the only way to receive results. Figure 7
shows how a callback in a call expression is destructured.
There are the following preconditions that a callback in a
call expression can be destructured;
1) the last parameter of the call expression should
be a callback function,
2) the call expression should not be a part of any
other expression,
3) the callback function should be error-first,
4) function body of the callback function should
contain if statement for not-null checking of the
first parameter, and
5) nominal procedure and error handling in the call-
back function should be completely separated.
In the case of Fig. 7, call expression func(arg,
...) is finally replaced with a try statement that
contains: 1) in the try clause, statements for nominal
procedure including call expression func modified as
a callback-agnostic representation, and 2) statements for
error handling in the catch clause.
4.2. Refinements for API calls and platform se-
Escapin generates PSPs by conducting several refine-
ments of PIPs. The refinements are done by applying
AST platform-specific transformation rules defined for a
specific target platform. The transformations are mainly
achieved by using template engine babel-template5
with several AST manipulations. We can define the target
platform of our application in a configuration file. Cur-
rently, Escapin provides two types of target platforms:
aws and local6. The platform type is set to aws if we
want to deploy our application to Amazon Web Services
(AWS) and is set to local if we want to execute our
application as a process running on the localhost for
debugging source code before deploying clouds.
The code snippet shown in Fig. 3 is compiled as a
snippet that uses an HTTP client. Figure 8 shows the
compilation example using request, which is one of the
most major HTTP clients for Node.js. This snippet has a
callback-agnostic representation, so it is not executable
so far. However, it is transformed into an executable
representation by a posterior step described in Sect. 4.4.
Figure 9 shows the code snippet that is a com-
pilation result of Fig. 5 where the platform type is
set to aws. The declarations of objects are trans-
formed into snippets for creating and deleting resources
when process.argv[2] is set to ”create” and
delete”, respectively. The declarations of functions
are transformed into snippets to be deployed to a FaaS.
If the target platform is set to aws, object items
is transformed into an AWS DynamoDB table with
name ”items”, and function handle is modified as a
function expression that adds two additional parameters
context and callback and replaces return foo
and throw bar with callback(null, foo) and
callback(bar), respectively.
4.3. Determining function types
Escapin uses the Type Checker provided by TypeScript
[28] to get argument names and return value types of
functions declared by external modules. Nowadays, we
can use the Type Checker for most of the major modules
by getting declaration files from a huge repository [29]
of type definitions, or in modules downloaded from the
npm repository. In order to use the Type Checker, Escapin
converts JavaScript files into TypeScript and downloads
required type declarations.
For each function, Escapin selects one of the following
four types by checking its argument names and return
value types:
asynchronous: a function type that returns Promise.
6. As the future work, we plan to add other target platforms (e.g.,
gcp for Google Cloud Platform, azure for Microsoft Azure, onprem
for on-premise container engines, and so on).
1// import api from "http://path/to/swagger.yaml";
2import request from "request";
4// item = api.items[id];
5const {res, body} = request({
6 url: ‘${id}‘,
7 method: "get",
8 json: true
9 });
10 };
11 item = body;
Figure 8. A compilation example of Fig. 3 using request.
1// export const items = {};
2switch (process.argv[2]) {
3case "create":
4new AWS.DynamoDB().createTable({
5 TableName: "items",
6 KeySchema: ...
7 });
9case "delete":
10 new AWS.DynamoDB().deleteTable({
11 TableName: "items"
12 });
13 break;
14 default:
15 break;
16 }
17 // export function handle(req) {
18 export function handle(req, context, callback) {
19 const {name, item} = req.body;
21 //items[name] = item;
22 new AWS.DynamoDB().putItem({
23 TableName: "items",
24 Item: ...
25 });
27 try {
28 // return doSomething(item);
29 callback(null, doSomething(item));
30 } catch (err) {
31 // throw err;
32 callback(err);
33 }
34 }
Figure 9. A compilation example of Fig. 5 where the platform type is
set to aws.
error-first-callback: a function type that requires an
error-first callback as the last parameter. We determine if
the callback is error-first by checking the first parameter
if the type is Error or the name matches regex pattern
general-callback: a function type that requires callback
which is not error-first.
general: a function type other than the above three types.
For example, the function types of rp in Fig. 2 and
request in Fig. 8 are set to asynchronous and error-
first-callback, respectively.
4.4. Asynchronization
Escapin conducts asynchronization by looking into the
function types of each function call. In this section, we
describe AST transformations for the asynchronization on
each function type by example.
Figure 10 shows a transformation for the case that the
function type of func is asynchronous. Call expression
func(arg) in the statement is replaced with the dec-
1 anyStatement(func(arg));
1const _t=await func(arg);
2 anyStatement(_t);
Figure 10. asynchronous
1function func(arg) {
2await anyFunc(item);
3 }
1async function func(arg) {
2await anyFunc(item);
3 }
Figure 11. Changing from general to
1const {p1, p2} = func(arg);
2 anyStatement(p1, p2);
1const _t=await new Promise(
2 (resolve, reject) => {
3 func(arg, (err, p1, p2) => {
4if (err) reject(err);
5else resolve({p1, p2});
6 }
7 });
8 anyStatement(_t.p1, _t.p2);
Figure 12. error-first-callback.
1 func(item => {
2const ret = await anyFunc(item);
3 anyStatement(ret);
4 });
1import deasync from "deasync";
2 func(item => {
3let _t, _done = false;
4 anyFunc(item).then(data => {
5 _t = data;
6 }).catch(err => {
7throw err;
8 }).finally(() => {
9 _done = true;
10 });
11 deasync.loopWhile(() => !_done);
12 const ret = _t;
13 anyStatement(ret);
14 });
Figure 13. general-callback.
1const result = => await anyFunc(item));
2 items.forEach(item => await anyFunc(item));
1const result = await Promise.all(
2async item => await anyFunc(item)));
3 items.forEach(async item => await anyFunc(item));
Figure 14. Exceptions of general-callback:Array methods.
1for (const item of items) {
2await anyFunc(item);
3 anyStatement(item);
4 }
1const _p = [];
2for (const item of items) {
3 _p.push((async () => {
4await anyFunc(item);
5 anyStatement(item);
6 })());
7 }
8await Promise.all(_p);
Figure 15. Parallelization of for-of iterations.
laration of a temporary variable, e.g., _t, initialized by
func(arg) wrapped in an await expression.
Figure 11 shows a transformation for the case that
the function type of func is general. If there is some
call expression with await in the body of function
func, the function type of func should be changed to
Figure 12 shows a transformation for the case that
the function type of func is error-first-callback. The
variable declaration having call expression func(arg)
is replaced into another variable declaration that initializes
a temporary variable as await new Promise(...)
containing the call expression of error-first-callback
function func with a callback function. The callback
function has parameters: err and the variables located
at the original variable declaration, e.g., p1, p2. The
callback function simply calls reject(err) if an error
occurred or resolve(...) with the rest parameters
packing into an object if succeeded. For statements after
the replacement in the example, the original variables p1
and p2 are replaced with _t.p1 and _t.p2, respec-
Figure 13 shows a transformation for the case that
the function type of func is general-callback. In the
case of Fig. 13, we cannot put async on the callback
function of func, and asynchronous function calls in the
callback function should be synchronized. This is because
the implementation of func does not expect that the
callback function returns Promise. Then, we use a li-
brary such as deasync7for synchronizing asynchronous
functions. The await expression in the original state-
ment is replaced with a temporary variable, e.g., _t, and
The await expression of function anyFunc is replaced
with the chains of Promise functions then,catch,
and finally, removing await. The resolved value of
function anyFunc is assigned to the temporary variable.
There are several exceptions in the transformations for
general-callback functions. Array methods such as map
and forEach can be considered as the exceptions. Figure
14 shows the cases of map and forEach. Function
map with a callback function to be added async due
to containing asynchronous function calls returns an array
of Promises. Therefore, by wrapping
to await Promise.all(), we can simply wait for
all callback function calls finished. Function forEach is
basically considered that callback function calls can be
executed asynchronously. Therefore, we can simply add
async and await as shown in Fig. 14.
Escapin also provides an optimization regarding paral-
lelization. Figure 15 shows an example of parallelization
of for-of iterations. This rule can be applied if the
iterations do not modify variables declared outside its
scope. Then, statements in the iteration are wrapped in
a new asynchronous function. Promise value returned
from the function is inserted into array _p, and await
Promise.all(_p) is added just after the for-of
statement. This rule is applicable not only for-of but
also for,for-in,while and do-while.
5. Evaluation
To evaluate the effectiveness of Escapin, we conducted
an experimental study to answer the following research
RQ1 (Effectiveness) To what degree does Escapin
reduce the amount of time to develop appli-
cations using APIs and cloud services?
RQ2 (Source code simplicity) To what degree can
engineers write simple source code by using
We implemented Escapin by using the latest version
of the following packages as of April 2018: Node.js
LTS, Yarn [30], Babel, Webpack [31], TypeScript and
SwaggerParser [32].
We designed a task of an application development that
consumes the Uber API [20], which is the most famous
ridesharing service (see Section 5.1).
We created a mock server of the Uber API8on AWS
by using Escapin. The API server only provides limited
functionality for riders: Ride Products and Ride Estimates.
We also created a test API server on AWS by using Es-
capin. The test API server accepts a request enclosing the
endpoint URI of the application for checking whether or
not the application correctly conforms to the specification.
We provided API documentation of those APIs by using
Swagger UI [33].
In order to confirm the effectiveness of Escapin, we
designed the experiment such that subjects do the task in
two cases: Case 1 is to develop the application by using
any existing tools without Escapin, and Case 2 is by using
Escapin. For the experiment, we gathered ten subjects who
are software engineers working at our affiliation.
We assume that there exist common operations be-
tween Case 1 and Case 2. For example, checking response
parameters of Uber API, design and implementation of the
logic to make the summarized JSON data from response
parameters, can be regarded as common operations. After
subjects do the common operations once, they can just
skip or reuse them. Therefore, we assume if all subjects
do the task in the same order of the cases, for the latter
case, the development time must get shorter than that in
the case of not doing the former case before. In order
to evaluate the effectiveness of Escapin more fairly, we
separated the ten subjects into two groups that do the task
in different order of the cases; subjects in Group 1 do
Case 1 first, and then Case 2, and subjects in Group 2
do tasks in the reverse order.
In order that all subjects independently do the tasks
at their own workspaces, we used the test API for not
only testing applications they wrote, but also measuring
development time instead of a more standard methodology
such as video transcripts. For defining the start time, we
instructed subjects to send requests to the test API with an
empty value when they begin the tasks. We defined the
development time as the time interval between the start
time and the time that first passed the test by the test
API. The measured time does not include the time for
learning how to use AWS API Gateway, Lambda, HTTP
clients on Node.js such as request, and also Escapin;
the subjects did begin the tasks after they judged they had
learned enough to accomplish the tasks. They took several
breaks during the tasks, so in order to exclude break time
from the development time, we instructed them to send
requests to the test API when they left from and returned
to the experiment.
Q1 How many years do you have programming experience?
Q2 Which programming languages do you usually use?
Q3 Which HTTP client did you use in Case 1?
Q4 Which libraries did you use with the HTTP client in Case 1?
Q5 Did you enjoy programming in Case 1?
Q6 Did you enjoy programming in Case 2?
After finishing the task, the subjects answered a ques-
tionnaire to investigate subjects’ characteristics and im-
pressions of Escapin and the experiment. Table 2 shows
the detail of the questionnaire. Q5 and Q6 are at a 7-
point scale with scores of 1 and 7 representing ”strongly
disagree” and ”strongly agree”, respectively.
Table 3 shows the subjects’ answers to the ques-
tionnaire. According to the answers, we found the sub-
jects have diverse experiences of programming languages,
and six out of ten subjects answered that they usually
use JavaScript, and they used various HTTP clients and
other libraries. By looking into the source code9, they all
used Promise, but only two subjects, C and J, used
async/await. This is because until April 1st, 2018,
AWS Lambda only supported Node.js runtime versions
up to 6.10, which do not support async/await10. Only
subject C did polyfill the source code by using Babel, and
only subject J could begin to the task after Node.js 8.10
runtime had been available. We also found that six out of
the ten subjects preferred Escapin to existing tools.
5.1. Task: Summarizing estimations of Uber
Develop an API GET /rough_estimates con-
forming to the following API specification:
Spec. 1 The API should be deployed on AWS API Gate-
way and Lambda.
Spec. 2 The API has four query parameters, which in-
dicate the latitude and longitude of the start and end
Spec. 3 The API conducts the following procedure:
1) Get estimate and product_id of an
available car nearby by requesting GET
/estimates/time with the query parameters.
2) Rename estimate to time.
3) Get the car information description,
capacity and image by requesting GET
/estimates/{id}with product_id
obtained from GET /estimates/time.
4) Get price estimation information by requesting
GET /estimates/price with the query pa-
5) From the price estimation information, get
estimate and distance that correspond
to each product_id obtained from GET
6) Exclude entries that estimate is set to
"Metered", which means this is a taxi.
7) Rename estimate to price.
8) Format the data as a JSON object and return it.
9. Source code of the applications written by the subjects, the
mock server, and the test API server can be downloaded from
10. A post of AWS Compute Blog on April 2nd, 2018.
Subject Q1 Q2 Q3 Q4 Q5 Q6
Group 1
A 3–5 JavaScript,Java request async 3 5
B 5–10 Java request async 4 4
C 10+ JavaScript,Python,Go request-promise-native babel, webpack 4 4
D 10+ JavaScript,Python node-fetch 4 4
E 10+ Java,Ruby sync-request 3 5
Group 2
F 10+ JavaScript,C/C++ request async 3 6
G 10+ JavaScript,Java,Python,Ruby request 3 5
H 1–3 JavaScript,Python node-fetch 3 5
I 1–3 Haskell axios 4 5
J 3–5 Python,Go request-promise serverless 5 5
Subject Case 1 Case 2
Group 1
A414.77 209.63
B 200.29 109.57
C 80.59 65.58
D 174.66 82.41
E 127.06 69.44
Average 199.47 107.32
Group 2
F 226.05 175.17
G 308.89 168.35
H 167.43 97.07
I 329.29 181.65
J 298.52 165.07
Average 266.03 157.46
p-value 0.00016
Cohen’s d11.66398
Observed power 0.99179
Subject LOC Cyclomatic Cognitive
Case 1 Case 2 Case 1 Case 2 Case 1 Case 2
A 101 42 29 7 30 12
B 108 50 20 7 16 12
C 73 29 8 6 1 0
D 49 33 6 8 14 11
E 82 58 19 10 14 6
F 112 49 22 5 8 1
G 164 87 29 8 32 17
H 93 24 29 4 22 3
I 59 37 11 5 0 7
J 72 29 8 5 8 7
Average 91.3 24.2 18.1 6.5 14.5 7.6
p-value 0.00003 0.00181 0.01384
Cohen’s d9.61006 5.04810 2.47486
Observed power 0.99995 0.99994 0.90048
5.2. Effectiveness (RQ1)
Table 4 shows the measured development time for
each subject in each case. Assuming the statistical popu-
lation of the development time in both cases would follow
a normal distribution, we conducted a paired-sample one-
tailed t-test of the null hypothesis that the difference
between development time of the same subject in two
cases has a mean value zero. The alternative hypothesis is
that there exists a statistically significant decrease in the
development time in Case 2 compared with that in Case
1. The results show that the null hypothesis is rejected
under significance level α= 0.05 which is typically used
in various kinds of studies. We also calculated Cohen’s d
[34] of the statistics in order to confirm the effect size.
According to the Sawilowsky’s definition [35], the effect
size reveals a huge (>2.0) effect to the development
time. We conducted the post-hoc analysis and found the
statistical power of 0.99179 is quite sufficient to consider
the result is reliable under the criterion that probability of
making Type II error (i.e., false negative) should be no
more than 20 percent [36].
We found that the average development time of Group
2is longer than that of Group 1 in both cases. We
consider this includes the effects of common operations
and a bias in skill levels between Group 1 and Group
2. Although we cannot precisely identify those effects so
far, we can generally say that there is the reduction effect
of development time in both groups.
Answer to RQ1
 
Escapin decreases development time with a huge
effect compared with existing tools.
 
5.3. Source code simplicity (RQ2)
We intuitively recognize that the smaller both the
number of lines and complexity of source code are, the
much simpler the source code is. Thus, we selected the
following metrics regarding source code simplicity: lines
of code (LOC), cyclomatic complexity [21], and cognitive
complexity [22]. We used SonarQube [37] for measuring
the three metrics of each subject’s source code.
Table 5 shows the measured values of the metrics.
As with the development time, we assume the statistical
populations of the three metrics in both cases would
follow normal distributions. We conducted paired-sample
one-tailed t-tests for each metric. The null hypothesis in
the test regarding a metric is that the difference between
the measured value of the same subject in two cases
has a mean value zero, and the alternative hypothesis is
that there exists a statistically significant decrease in the
measured value in Case 2 compared with that in Case
1. The results show that the null hypothesis is rejected
under significance level α= 0.05. We calculated Cohen’s
dfor each metric, and they reveal huge (>2.0) effects
to both the lengths and the complexity of the source
code. According to the post-hoc analysis, we found the
statistical power of 0.9995 and 0.9994 regarding LOC
and cyclomatic complexity, respectively, is quite sufficient
to consider the results are reliable. Regarding cognitive
complexity, although the statistical power of 0.90048 is
smaller than the other metrics, it is still reasonable under
the criterion [36].
Answer to RQ2
 
Escapin simplifies source code with a huge effect
compared with existing tools.
 
5.4. Discussion
The subjects’ source code of Case 1 looks the subjects
painstakingly wrote asynchronous processing using vari-
ous nested flow-break structures such as if,for,catch.
The results mainly reveal the difficulty of implementing
data manipulations with asynchronous API calls. We con-
sider that importing APIs as objects and callback-agnostic
programming by Escapin really contributed to making it
much easier to be implemented correctly. We can read
most of the source code of Case 2 as just JSON data
manipulations including several Array methods.
Regarding the usage of AWS, they struggled to find
out how to get a return value from Lambda functions due
to inappropriate use of the asynchronous features such
as Promise,async/await. We observed the trial-and-
error process was unavoidable even if they took a support
to use AWS such as Serverless Framework [13] (see the
source code of subject J). We consider that the abstraction
of AWS resources by using the exporting feature of Es-
capin contributed to reducing the efforts by skipping the
Lack of auxiliary tools such as linter and code com-
pletion negatively affects to the developer experience of
Escapin. There is no difference between the satisfaction
of using Escapin and other tools in four out of the ten
subjects. After the evaluation, most of them mentioned
that it is still difficult to do debugging PIPs without any
assistance in source code editor.
5.5. Threats to validity
There are threats to internal validity. The first threat
is that there are only ten subjects participated in the
evaluation. The potential problem of it is that the actual
statistical populations would follow other kinds of dis-
tributions than a normal distribution, and the result of
the statistical test might be changed. In order to con-
firm the effectiveness of the Escapin more generally and
precisely, we need to conduct further evaluations with a
number of subjects sufficient for the interval estimation
at an acceptable confidence level. The second threat is
the precision of measuring development time by using
the test API. We consider doing the task at their own
workspace can measure more real effectiveness than a
designated environment for recording subjects’ activities
by video cameras. However, the precision of the measure-
ment might be decreased from video transcripts due to a
lag between the time they sent requests to the test API
and that they actually began the task. The third threat is
the effect of taking several breaks during the experiment.
When a subject taking a break returns to the task, he/she
should do context switching in his/her mind, which may
be heavy for him/her.
There are threats to external validity in the task design.
In the evaluation, we only have the one and small task
using Uber API. The task has less complexity than other
real applications; application in the task only consumes
APIs with GET method and does not use any storage
service. Therefore, we consider the evaluation is still not
sufficient to prove the generality and applicability of our
approach. We need to confirm it using various kinds of
tasks as the future work.
6. Related work
There are various transpilers that generate JavaScript
programs. Babel [25] is used for obtaining the latest
ECMAScript features not supported by web browsers and
runtimes. TypeScript [28], CoffeeScript [38] and Dart
[39] are transpilers that transform programs written in a
different language specification into JavaScript programs.
Moreover, there are frameworks that utilize transpilers.
React [40] and Vue.js [41] is a framework that introduces
an extension to JavaScript specification for simplifying
frontend application development. Flow [42], [43] is a
static type checker for JavaScript that introduces type
annotations in JavaScript programs. Webpack [31] bundles
dependencies of a JavaScript program into a minified one.
In addition, there are various libraries based on JavaScript
AST manipulations [44], [45]. For Escapin just introduces
new semantics in existing JavaScript syntax, we can use
Escapin by combining with those tools without any con-
There are various kinds of transpilers that transform a
program into a program in a different language [46]–[48],
one in a different version of the language [49] and one
refined for obtaining dependability [50], [51], simplicity
There are several automated approaches for configur-
ing cloud resources. AWS provides various tools [16]–[19]
for simplifying resource configurations and provisioning.
There are several tools [13], [53] that target developing
serverless applications that consist of a combination of
resources on API gateway, FaaS, and several managed
services. Escapin is similar to that kind of tools but has
an advantage of simplifying programming over the other
There are several libraries for the simple representation
of asynchronous processing in JavaScript. RxJS [54] is
a library that provides reactive programming, which is a
programming model suitable for asynchronous processing.
Node-RED [55] is a programming tool that has a graphical
editor for wiring building blocks for some processing
and various event sources as event-driven non-blocking
applications and is mainly targeted to the Internet of
Things. The scopes of them are different from that of
Escapin; our scope is to simplify the application logic
using cloud services and APIs.
Understanding asynchronous API calls in JavaScript
is a challenging problem [56]–[58]. Asynchronous pro-
cessing using APIs is also error-prone [59]. Our approach
is capable to liberate us from the complexity of asyn-
chronous processing in JavaScript.
7. Conclusions
We proposed Escapin, a JavaScript transpiler for de-
veloping application programs that consume APIs and
are deployed on cloud services. Escapin provides a sim-
pler programming experience than other existing tools
by introducing new features: importing APIs as objects
and exporting objects and functions as cloud service
resources. Moreover, callback-agnostic programming in
Escapin makes programs much simpler. Experimental re-
sults show that Escapin statistically significantly reduces
development time and also simplifies source code with
huge effects compared with other existing tools.
As the future work, we need to conduct further evalua-
tions to confirm the effectiveness and source code simplic-
ity more generally and precisely by conducting evaluations
on various kinds of tasks with a number of subjects.
Moreover, we also need to confirm the applicability of the
transformation rules, which need several preconditions,
e.g., error-first callbacks should have the first argument
that matches the regex pattern and start with a not-null
check of the first argument, etc.
Currently, we have not yet provided auxiliary tools
such as linter, code completion, testing and debugging
for Escapin, and Escapin does not support cloud services
other than AWS. As the future work, we will develop
such tools and add transformation rules for other clouds.
Moreover, we will come up with further transpiling op-
timizations in order to satisfy given constraints regarding
non-functional requirements.
[1] Amazon Web Services. [Online]. Available:
[2] Google Cloud Platform. [Online]. Available:
[3] Microsoft Azure. [Online]. Available:
[4] M. Vukovic, J. Laredo, V. Muthusamy, A. Slominski, R. Vaculin,
W. Tan, V. Naik, I. Silva-Lepe, A. Kumar, B. Srivastava, and
J. W. Branch, “Riding and Thriving on the API Hype Cycle,”
Commun. ACM, vol. 59, no. 3, pp. 35–37, Feb. 2016. [Online].
[5] P. Mell, T. Grance et al., “The NIST definition of cloud comput-
ing,” 2011.
[6] Philips Hue API. [Online]. Available: https://www.developers.
[7] Amazon Mechanical Turk. [Online]. Available: https://www.mturk.
[8] Freelancer. [Online]. Available:
[9] E. Reis, “The Lean Startup,” New York: Crown Business, 2011.
[10] Node.js. [Online]. Available:
[11] S. M. Sohan, F. Maurer, C. Anslow, and M. P. Robillard, “A study
of the effectiveness of usage examples in REST API documenta-
tion,” in 2017 IEEE Symposium on Visual Languages and Human-
Centric Computing (VL/HCC), Oct 2017, pp. 53–61.
[12] Callback Hell, A guide to writing asynchronous JavaScript
programs. [Online]. Available:
[13] Serverless Framework. [Online]. Available:
[14] “OpenAPI Generator.” [Online]. Available:
[15] “Swagger-JS.” [Online]. Available:
[16] AWS CloudFormation. [Online]. Available:
[17] AWS Serverless Application Model (AWS SAM). [Online].
[18] AWS Step Functions. [Online]. Available:
[19] Amazon Simple Workflow Service. [Online]. Available: https:
[20] Uber API. [Online]. Available:
[21] T. J. McCabe, “A Complexity Measure,IEEE Transactions on
Software Engineering, vol. SE-2, no. 4, pp. 308–320, Dec 1976.
[22] “Cognitive Complexity.” [Online]. Available:
[23] AWS Lambda. [Online]. Available:
[24] Promises. [Online]. Available:
[25] Babel. [Online]. Available:
[26] B. Mulloy, “Web api design,” 2013.
[27] R. T. Fielding, “Architectural styles and the design of
network-based software architectures,” Ph.D. dissertation, 2000,
[28] TypeScript. [Online]. Available:
[29] DefinitelyTyped: The repository for high quality TypeScript type
definitions. [Online]. Available:
[30] Yarn. [Online]. Available:
[31] webpack. [Online]. Available:
[32] Swagger Parser. [Online]. Available:
[33] “Swagger UI.” [Online]. Available:
[34] J. Cohen, Statistical Power Analysis for the Behavioral Sciences.
Lawrence Erlbaum Associates, 1988.
[35] S. S. Sawilowsky, “New effect size rules of thumb,Journal of
Modern Applied Statistical Methods, vol. 8, no. 2, p. 26, 2009.
[36] P. D. Ellis, The Essential Guide to Effect Sizes: Statistical Power,
Meta-Analysis, and the Interpretation of Research Results. Cam-
bridge University Press, 2010.
[37] “SonarQube: Continuous Code Quality.” [Online]. Available:
[38] CoffeeScript. [Online]. Available:
[39] Dart. [Online]. Available:
[40] React. [Online]. Available:
[41] Vue.js. [Online]. Available:
[42] A. Chaudhuri, P. Vekris, S. Goldman, M. Roch, and G. Levi,
“Fast and Precise Type Checking for JavaScript,Proc. ACM
Program. Lang., vol. 1, no. OOPSLA, pp. 48:1–48:30, Oct. 2017.
[Online]. Available:
[43] Flow. [Online]. Available:
[44] ESLint. [Online]. Available:
[45] Tern.js. [Online]. Available:
[46] jsweet. [Online]. Available:
[47] VOC. [Online]. Available:
[48] jack. [Online]. Available:
[49] G. Antal, D. Havas, I. Siket, ´
A. Besz´
edes, R. Ferenc, and J. Mihal-
icza, “Transforming c++11 code to c++03 to support legacy com-
pilation environments,” in 2016 IEEE 16th International Working
Conference on Source Code Analysis and Manipulation (SCAM),
Oct 2016, pp. 177–186.
[50] A. Benso, S. Chiusano, P. Prinetto, and L. Tagliaferri, “A C/C++
source-to-source compiler for dependable applications,” in Pro-
ceeding International Conference on Dependable Systems and
Networks. DSN 2000, 2000, pp. 71–78.
[51] M. Rebaudengo, M. S. Reorda, M. Violante, and M. Torchiano, “A
source-to-source compiler for generating dependable software,” in
Proceedings First IEEE International Workshop on Source Code
Analysis and Manipulation, 2001, pp. 33–42.
[52] Y. Higo, A. Ohtani, and S. Kusumoto, “Generating Simpler AST
Edit Scripts by Considering Copy-and-paste,” in Proceedings of
the 32Nd IEEE/ACM International Conference on Automated
Software Engineering, ser. ASE 2017. Piscataway, NJ, USA:
IEEE Press, 2017, pp. 532–542. [Online]. Available: http:
[53] Chalice. [Online]. Available:
[54] “RxJS.” [Online]. Available:
[55] Node-RED. [Online]. Available:
[56] S. Alimadadi, A. Mesbah, and K. Pattabiraman, “Understand-
ing Asynchronous Interactions in Full-Stack JavaScript,” in 2016
IEEE/ACM 38th International Conference on Software Engineering
(ICSE), May 2016, pp. 1169–1180.
[57] S. Alimadadi, S. Sequeira, A. Mesbah, and K. Pattabiraman,
“Understanding JavaScript Event-Based Interactions with
Clematis,” ACM Trans. Softw. Eng. Methodol., vol. 25,
no. 2, pp. 12:1–12:38, May 2016. [Online]. Available:
[58] Wittern, Erik and Ying, Annie T. T. and Zheng, Yunhui and
Dolby, Julian and Laredo, Jim A., “Statically Checking Web API
Requests in JavaScript,” in Proceedings of the 39th International
Conference on Software Engineering, ser. ICSE ’17. Piscataway,
NJ, USA: IEEE Press, 2017, pp. 244–254. [Online]. Available:
[59] J. Wang, W. Dou, Y. Gao, C. Gao, F. Qin, K. Yin, and J. Wei, “A
comprehensive study on real world concurrency bugs in Node.js,
in 2017 32nd IEEE/ACM International Conference on Automated
Software Engineering (ASE), Oct 2017, pp. 520–531.
Full-text available
In this paper we present the design and implementation of Flow, a fast and precise type checker for JavaScript that is used by thousands of developers on millions of lines of code at Facebook every day. Flow uses sophisticated type inference to understand common JavaScript idioms precisely. This helps it find non-trivial bugs in code and provide code intelligence to editors without requiring significant rewriting or annotations from the developer. We formalize an important fragment of Flow's analysis and prove its soundness. Furthermore, Flow uses aggressive parallelization and incrementalization to deliver near-instantaneous response times. This helps it avoid introducing any latency in the usual edit-refresh cycle of rapid JavaScript development. We describe the algorithms and systems infrastructure that we built to scale Flow's analysis.
Many JavaScript applications perform HTTP requests to web APIs, relying on the request URL, HTTP method, and request data to be constructed correctly by string operations. Traditional compile-time error checking, such as calling a non-existent method in Java, are not available for checking whether such requests comply with the requirements of a web API. In this paper, we propose an approach to statically check web API requests in JavaScript. Our approach first extracts a request's URL string, HTTP method, and the corresponding request data using an inter-procedural string analysis, and then checks whether the request conforms to given web API specifications. We evaluated our approach by checking whether web API requests in JavaScript files mined from GitHub are consistent or inconsistent with publicly available API specifications. From the 6575 requests in scope, our approach determined whether the request's URL and HTTP method was consistent or inconsistent with web API specifications with a precision of 96.0%. Our approach also correctly determined whether extracted request data was consistent or inconsistent with the data requirements with a precision of 87.9% for payload data and 99.9% for query data. In a systematic analysis of the inconsistent cases, we found that many of them were due to errors in the client code. The here proposed checker can be integrated with code editors or with continuous integration tools to warn programmers about code containing potentially erroneous requests.
Web applications have become one of the fastest-growing types of software systems today. Despite their popularity, understanding the behavior of modern web applications is still a challenging endeavor for developers during development and maintenance tasks. The challenges mainly stem from the dynamic, event-driven, and asynchronous nature of the JavaScript language.We propose a generic technique for capturing low-level event-based interactions in a web application and mapping those to a higher-level behavioral model. This model is then transformed into an interactive visualization, representing episodes of triggered causal and temporal events, related JavaScript code executions, and their impact on the dynamic DOM state. Our approach, implemented in a tool called CLEMATIS, allows developers to easily understand the complex dynamic behavior of their application at three different semantic levels of granularity. Furthermore, CLEMATIS helps developers bridge the gap between test cases and program code by localizing the fault related to a test assertion. The results of our industrial controlled experiment show that CLEMATIS is capable of improving the comprehension task accuracy by 157% while reducing the task completion time by 47%. A follow-up experiment reveals that CLEMATIS improves the fault localization accuracy of developers by a factor of two.
Conference Paper
JavaScript has become one of the most popular languages in practice. Developers now use JavaScript not only for the client-side but also for server-side programming, leading to "full-stack" applications written entirely in JavaScript. Understanding such applications is challenging for developers, due to the temporal and implicit relations of asynchronous and event-driven entities spread over the client and server side. We propose a technique for capturing a behavioural model of full-stack JavaScript applications' execution. The model is temporal and context-sensitive to accommodate asynchronous events, as well as the scheduling and execution of lifelines of callbacks. We present a visualization of the model to facilitate program understanding for developers. We implement our approach in a tool, called Sahand, and evaluate it through a controlled experiment. The results show that Sahand improves developers' performance in completing program comprehension tasks by increasing their accuracy by a factor of three.