For Async.js Users
Last updated
Was this helpful?
Last updated
Was this helpful?
is a popular utility module which provides straight-forward, powerful functions for working with asynchronous JavaScript. Async provides around 20 functions that include the usual 'functional' suspects (map, reduce, filter, each...) as well as some common patterns for asynchronous control flow (parallel, series, waterfall...). All these functions assume you follow the node.js convention of providing a single callback as the last argument of your async function.
Many of these concepts in the library map directly to RxJS concepts. We'll go operator by operator on how each map to existing functionality in RxJS.
async.each
##In this example, we will use async.each
to iterate an array of files to write some contents and save.
Using RxJS, you can accomplish this task in a number of ways by using Rx.Observable.fromNodeCallback
to wrap the fs.writeFile
function, and then iterate the files by using the Rx.Observable.for
method.
async.map
##The async.map
method produces a new array of values by mapping each value in the given array through the iterator function. The iterator is called with an item from the array and a callback for when it has finished processing. The callback takes 2 arguments, an error and the transformed item from the array. If the iterator passes an error to this callback, the main callback for the map function is immediately called with the error.
In this example, we'll get the fs.stat
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.stat
method again using our Rx.Observable.fromNodeCallback
, then iterate using the Rx.Observable.for
method, and finally, calling .toArray()
to get our results as an entire array.
async.filter
##The async.filter
method returns a new array of all the values which pass an async truth test. The callback for each iterator call only accepts a single argument of true or false, it does not accept an error argument first! This is in-line with the way node libraries work with truth tests like fs.exists.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.exists
method using our Rx.Observable.fromCallback
as it only has one result, a true
or false
value instead of the usual callback with error and result. Then we'll iterate using the Rx.Observable.for
method, filter the existing files and finally, calling .toArray()
to get our results as an entire array.
async.reject
##The async.reject
method is the opposite of filter. Removes values that pass an async truth test.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.exists
method using our Rx.Observable.fromCallback
as it only has one result, a true
or false
value instead of the usual callback with error and result. Then we'll iterate using the Rx.Observable.for
method, filter the existing files using filter
and finally, calling .toArray()
to get our results as an entire array.
async.reduce
##The async.reduce
method reduces a list of values into a single value using an async iterator to return each successive step. Memo is the initial state of the reduction. This function only operates in series. For performance reasons, it may make sense to split a call to this function into a parallel map, then use the normal Array.prototype.reduce
on the results. This function is for situations where each step in the reduction needs to be async, if you can get the data before reducing it then it's probably a good idea to do so.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
In RxJS, we have a number of ways of doing this including using Rx.Observable.fromArray
to turn an array into observable sequence, then we can call reduce
to add the numbers. To ensure that it is indeed async, we can switch to the Rx.Scheduler.timeout
to ensure that it is done via a callback.
async.detect
##The async.detect
method returns the first value in a list that passes an async truth test. The iterator is applied in parallel, meaning the first iterator to return true will fire the detect callback with that result.
In this example, we'll get the first file that matches.
In RxJS, we can iterate over the files as above using Rx.Observable.for
and then calling first
to get the first matching file project forward the file name and whether the file exists.
async.some
##The async.some
method returns true
if at least one element in the array satisfies an async test. The callback for each iterator call only accepts a single argument of true or false, it does not accept an error argument first! This is in-line with the way node libraries work with truth tests like fs.exists. Once any iterator call returns true, the main callback is immediately called.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.exists
method using our Rx.Observable.fromCallback
as it only has one result, a true
or false
value instead of the usual callback with error and result. Then we'll iterate using the Rx.Observable.for
method, then call some
to determine whether any match.
async.every
##The async.every
method returns true
if every element in the array satisfies an async test. The callback for each iterator call only accepts a single argument of true or false, it does not accept an error argument first! This is in-line with the way node libraries work with truth tests like fs.exists.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.exists
method using our Rx.Observable.fromCallback
as it only has one result, a true
or false
value instead of the usual callback with error and result. Then we'll iterate using the Rx.Observable.for
method, then call every
to determine whether all match.
async.concat
##The async.concat
method applies an iterator to each item in a list, concatenating the results. Returns the concatenated list. The iterators are called in parallel, and the results are concatenated as they return.
In this example, we'll determine whether the file exists by calling fs.exists
for each file given and have the results returned as an array.
Using RxJS, we can achieve the same results of an array of all of our values by wrapping the fs.readdir
method using our Rx.Observable.fromNodeCallback
. Then we'll iterate using the Rx.Observable.for
method, then call reduce
to add each item to the item to the overall list.
async.series
##The async.series
runs an array of functions in series, each one running once the previous function has completed. If any functions in the series pass an error to its callback, no more functions are run and the callback for the series is immediately called with the value of the error. Once the tasks have completed, the results are passed to the final callback as an array.
It is also possible to use an object instead of an array. Each property will be run as a function and the results will be passed to the final callback as an object instead of an array. This can be a more readable way of handling results from async.series.
In this example we'll run some examples with both an array or an object.
We can achieve the same functionality of async.series
with an array by simply calling fromArray and calling flatMap
to give us the observable of the current. Then we'll call reduce
to add each item to a new array to return.
Using an object literal can also be achieved with a little bit more work, but totally reasonable. Instead of just returning the observable in flatMap
, we'll add a property to a new object which will contain our key moving forward. Then, we'll call reduce
much as before, copying the values to a new object, and then plucking the value from each time it comes through and adding it to our final object.
async.parallel
##The async.parallel
runs an array of functions in parallel, without waiting until the previous function has completed. If any of the functions pass an error to its callback, the main callback is immediately called with the value of the error. Once the tasks have completed, the results are passed to the final callback as an array.
It is also possible to use an object instead of an array. Each property will be run as a function and the results will be passed to the final callback as an object instead of an array. This can be a more readable way of handling results from async.parallel.
In this example we'll run some examples with both an array or an object.
We can achieve the same functionality of async.series
with an array by calling Rx.Observable.forkJoin
with our array of observable sequences. This returns the last value from each sequence in "parallel".
Using an object literal can also be achieved with a little bit more work, but totally reasonable. Instead of simply calling forkJoin
, we first need to extract the observable sequences by calling map
on the keys we obtained by Object.keys
. Because the order of observable sequences is deterministic, we can then call map
to transform the array into an object, by calling reduce
on the array, turning the array into an object with the appropriate keys.
async.whilst
##The async.whilst
method repeatedly call function, while test returns true. Calls the callback when stopped, or an error occurs.
In this example we'll just run a keep calling the callback while the count is less than 5.
We can achieve the same kind of functionality by using the Rx.Observable.while
method which takes a condition and an observable sequence that we created by calling Rx.Observable.create
.
async.doWhilst
##The async.doWhilst
method is a post check version of whilst
. To reflect the difference in the order of operations test and fn arguments are switched. doWhils
t is to whilst
as do while
is to while
in plain JavaScript.
In this example we'll just run a keep calling the callback while the count is less than 5.
We can achieve the same kind of functionality by using the doWhile
on our observable sequence which takes a predicate to determine whether to continue running.
async.nextTick
##The async.nextTick
method calls the callback on a later loop around the event loop. In node.js this just calls process.nextTick, in the browser it falls back to setImmediate(callback) if available, otherwise setTimeout(callback, 0), which means other higher priority events may precede the execution of the callback.
In this example we'll just run a keep calling the callback while the count is less than 5.
We can achieve the same thing by using the Rx.Scheduler.timeout
scheduler to schedule an item which will optimize for the runtime, for example, using process.nextTick
if available, or setImmediate
if available, or other fallbacks like MessageChannel
, postMessage
or even an async script load.
async.waterfall
##The async.waterfall
method runs an array of functions in series, each passing their results to the next in the array. However, if any of the functions pass an error to the callback, the next function is not executed and the main callback is immediately called with the error.
We can easily accomplish the same task as above using our wrappers for Rx.Observable.fromCallback
and Rx.Observable.fromNodeCallback
, creating a waterfall-like method. First, let's implement a waterfall
method using plain RxJS in which we enumerate the functions and call flatMapLatest
on each resulting observable sequence to ensure we only get one value.
Once we've defined this method, we can now use it such as the following, wrapping fs.exists
, fs.rename
and fs.stat
.
async.compose
##Each function is executed with the this
binding of the composed function.
In this example, we'll chain together two functions, one to add 1 to a supplied argument, and then chain it to another to multiply the result by 3.
Using RxJS, we can accomplish this using the usual composition operator selectMany
or flatMap
. We'll wrap the setTimeout
with a wrapTimeout
method and ensure that we do deterministic cleanup via clearTimeout
. Then we can compose together our add1
and mul3
functions which result in observable sequences.
The method applies an iterator function to each item in an array, in parallel. The iterator is called with an item from the list and a callback for when it has finished. If the iterator passes an error to this callback, the main callback for the each function is immediately called with the error.
In this example, we'll check whether a file exists, then rename it and finally return its .
The method creates a function which is a composition of the passed asynchronous functions. Each function consumes the return value of the function that follows. Composing functions f(), g() and h() would produce the result of f(g(h())), only this version uses callbacks to obtain the return values.