Skip to content

Latest commit

 

History

History
137 lines (97 loc) · 5.33 KB

syntax.md

File metadata and controls

137 lines (97 loc) · 5.33 KB

Async and Await syntax and usage

The following sections show you how to declare, call and use async functions using the ES7 async and await keywords, as well as from existing ES6 and ES5 code.

Declaring Async Functions

To declare an asynchronous function, put async in front of the definition. async is an ES7 keyword. You shouldn't use it as a top level identifier (variable or function name) in ES7 code. This is how it looks:

	async function myFunc(args) {
		body ;
		return expr ;
	}

TRY-IT

(NB: There are other mappings too, like checking for nested functions and try catch blocks, but the essence is demonstrated in the example above).

async function myFunc(args) {
 	if (!args)
 		throw new Error("Missing parameters") ;
 	return doSomething(args) ;
}

TRY-IT

Like any function returning a Promise, you invoke the function and use the Promise via code such as:

myFunc(args).then(function(returnValue){
	-- do something --			// Success! Use returnValue
}, function(exception) {
	-- do something else 		// Bad! Handle the error
}) ;

Async invocation

Thhe ES7 keyword await is implemented as a unary prefix operator (in the same kind of place you might find 'typeof' or 'delete', and also before object member definitions). It is this transformation that stops all the crazy indenting that async callbacks generate.

var result = await myFunc(args) ;
moreStuff(result) ;

This is transformed into the code:

return myFunc(args).then(function($await_myFunc$1) {
	var result = $await_myFunc$1 ;
	moreStuff(result) ;
},$error) ;

TRY-IT

Awaiting multiple times

A statement or expression can combine multiple async results, for example:

console.log(await as1(1),await as2("hello")+await as1(3)) ;

TRY-IT

This is both syntactically and semantically meaningful, but in the current implementation will call the async functions serially (note: the order in which they are invoked is not guaranteed). It might well be more efficient in this case to use the 'map' cover function (see below) to execute all the functions in parallel first, and use the results:

var nodent = require('nodent')() ;
var map = nodent.require('map') ;

// Execute all the async functions at the same time
mapped = await map([as1(1),as2("hello"),as1(3)]) ;
// When they're done:
console.log(mapped[0],mapped[1]+mapped[2]) ;

Most Promise libraries have a function called Promise.all(), which is similar to nodent.map. nodent.map is more flexible in that Promise.all() only accepts arrays whereas map can map Objects and apply a specific async function to each value in the Array/Object. See below for more details and examples). Any values passed to map that are not Thenable (i.e. Promises or async function calls) are simply passed through unchanged.

Invoking async functions from ES5/6

As described above, the return type from an async function is a Promise (or, to be accurate it's whatever type you assign the scoped variable Promise - if this is nodent.Thenable, then it has a then() member, and behaves enough like a Promise to work with Promises/A+-compliant libraries). So, to invoke an async function from a normal ES5 script you can use the code:

// An async function defined somewhere
async function readFile() { ... }

// Calling it using ES5 syntax in a non-nodented way
readFile(filename).then(function(data){
    ...
},function(err){
	....
}) ;

Similarly, you can wait for any Promise with the awaitkeyword - i.e. not just functions you defined yourself as async:

// The elasticsearch library returns a Promise if you don't supply a callback
var resultPromise = elasticsearch.search(query) ;
console.log(await resultPromise) ;

or just:

console.log(await elasticsearch.search(query)) ;

Defining async functions from ES5

Use nodent! And any function returning a Promise (or Thenable) can be used with await.

Missing out await

Forgetting to put await in front of an async call is easy, and usually not what you want - you'll get a Promise. This can be useful though, when you need a reference to an async return value:

var fn ;
if (x)
	fn = test(x) ; // 'test' is async - don't await
else
	fn = testDefault() ;	// testDefault is async - don't await

return await fn ;	// Now await for which function fn refers to

async function f() { return true ; }
	
var x = f() ;	// 'x' = a Promise

// Call x using nodent
try {
	result = await x ;
	...
} catch(error) {
	...
}
	
// or call x from ES5/6
x.then(function(result){
 	...
},function(error){
 	...
}) ;

Remember that calling an async function without await still runs the functions - you just can't get the return value, except if use are using lazyThenables in -es7 mode.