Working with when.js and Promises in TypeScript

Last update: Edit

1 Introduction

On this page you will find an explanation about the use of promises in the Mendix SDK.

Some of the operations you perform when, for instance, loading a working copy in the Model Server, take some time to complete. In regular (synchronous) programming, you would have to wait for each operation to complete before you could start with the next one.

In an asynchronous programming paradigm, you can start operations in the background and continue with other stuff while you are waiting for the background jobs to complete. Traditional JavaScript uses callback functions to achieve this. However, these tend to become very complicated when you have multiple nested, callbacks. This phenomenon is known as “callback hell.” Hence, the need for promises .

A promise is an object that will eventually either get a value (the promise is fulfilled), or it will be rejected if something goes wrong. Promises can be chained, meaning that a command y will only be executed once the promise forx is fulfilled.

There are multiple libraries that implement promises in JavaScript; in the Mendix SDK we have chosen to use when.js.

2 How Do when.js Promises Map to TypeScript?

In the when.js documentation you will find many code examples that look like this:

// sayHello() is a function which returns a promise
var greetingPromise = sayHello();
greetingPromise.then(function (greeting) {
    console.log(greeting);    // 'hello world'

Even though this is still valid TypeScript, you have a more elegant way to write this. Instead, in Mendix SDK code you would write something like this:

var greetingPromise = sayHello();
greetingPromise.then( (greeting) => {
    console.log(greeting);    // 'hello world'

or even shorter:

var greetingPromise = sayHello();
greetingPromise.then( greeting => console.log(greeting) ); // 'hello world'

As you can see, specifying the function keyword isn’t required here. In TypeScript you can use arrow functions. In the above example, (greeting) is simply the list of parameters of the function that is defined after the =>. If there is just a single parameter, the parentheses may even be omitted. Likewise, when the function body is a single expression, the curly braces may be omitted.

3 How Do You Use Promises?

The returning pattern is that each time you receive a Promise object from a function call, you call then on it, until you end the chain with done. In done, you handle either the result if everything worked correctly, or you handle the error in case a promise was rejected somewhere along the way.

	.then(project => {
		myLog(`Created new project: ${ }: ${ }`);
		readlineSync.question("About to create online working copy...");

		return project.createWorkingCopy();
	.then(workingCopy => {
		myLog(`Created working copy: ${ }`);
		readlineSync.question("About to generate a domain model...");

		return generateApp(workingCopy);
	.then(workingCopy => {
		readlineSync.question("About to commit changes back to the Team Server...");
		return workingCopy.commit();
	() => {
		myLog("Done. Check the result in Studio Pro.");
	error => {
		console.log("Something went wrong:");

Note that workingCopy.commit() returns a Promise<[Revision](>. You ignore this value in our success handler (line 68), but you could have used it for instance to show the revision number to the user.

4 Common Pitfalls

4.1 Execution in the Wrong Order

In a piece of code that looks like the following snippet, it can happen that Done is printed to the console before loadAllDocuments() has finished.

function loadAllDocuments(documents: projects.IDocument[]): void {
	documents.forEach( doc => loadAsPromise(doc) );

// Assuming you already have a project at your disposal
  .then((workingCopy) => {
    let firstModule = workingCopy.model().allModules()[0];
  .done( () => console.log("Done"), errorHandler);

In this example, you use the loadAsPromise convenience function to load a module’s documents into memory. The body of loadAllDocuments() contains asynchronous code, but does not return a promise. Because of that, the log statement in the done block is executed immediately after loadAllDocuments() is invoked, before all documents have finished loading.

How to fix this? By creating a promise on top of the asynchronous calls, of course!

function loadAllDocumentsAsPromise(documents: projects.IDocument[]): when.Promise<projects.Document[]> {
  return when.all<projects.Document[]>( doc => loadAsPromise(doc)));

In the snippet above, doc => loadAsPromise(doc)) transforms the documents array into a Promise of an array of Documents. Using when.all, you return a promise that resolves only when all documents have been loaded. Only then you can make sure that you do not enter done() before the promise has been resolved.

4.2 this Binding

In an arrow function, the object this points to is different from what it would be in a regular function declaration.

In practice, this sometimes makes you prefer the function notation (for example, when you want to put a timeout on an integration test).

4.3 Potentially Unhandled Rejection

If your script exits with Potentially unhandled rejection somewhere in the output, then you have most likely started an asynchronous/background operation as a when.js promise, but you did not “finish” the promise chain with a done operation. You should always end like this:

	.done(callback, errorHandler); // Always end with 'done'

5 Resources