Enjoying Zousan – the fastest Promise implementation on the planet – but wish it had more accessory functionality? Check out Zousan-Plus!
When I wrote Zousan, the goal was a Promise A+ implementation that was very small and very fast. I wanted this for use in mobile sites, Internet of Things builds, and other resource-constrained environments. And of course, even with desktop browsers, servers and other resource-plentiful environments, streamlined code carries many benefits (faster to transfer, faster to execute, less code surface to test, more confidence in its security/reliability).
But often you will find yourself wanting extended functionality - particularly when writing code that is heavily asynchronous in nature (which generally should be the case for anything non-trivial). So I wrote a companion library: Zousan Plus.
Mostly what I have found myself wanting, is something that aids in dealing with standard patterns and workflows that involve promises. Such as mapping:
Take mapping for example. JavaScript coders often find themselves transforming an array via a function, as the Array.map function conveniently offers:
var myArray = [ 1, 2, 3, 4, 5, 6, 7 ]
var squares = myArray.map(function(value) {
return value * value })
Here, we generate an array of squares of the first 7 integers by mapping those integers over a function that squares them.
But what if the function we wish to map over the array returns a promise of a value rather than the value itself? The traditional Array.map could still be used, but would differ from what you might expect in the following three ways:
The first point is quite probably a non-issue, since "philosophically" a mapping function should not depend on being run serially - but it is important to understand this distinction.
The other two points can be easily handled using the Promise.all function available in most Promise implementations (including Zousan):
var myArray = [1, 2, 3, 4, 5, 6, 7]
var squares = Zousan.all(myArray.map(squareAsync))
What if other aspects of this pattern also involved promises? What if the original array itself was actually a promise of an array - and/or if one or more of the items within the array were promises?
All these cases are handled with the Zousan.map function provided by Zousan-plus:
// Returns a promise to resolve to album information of the album ID specified
function getAlbumInfo(albumId)
{
return ajaxCall(getAlbumQueryURL(albumId))
}
// Pass in an array of album IDs and you will get a promise which resolves to
// an array of album information objects respectively
function getMultipleAlbumInfo(albumIdArray)
{
return Zousan.map(albumIdArray, getAlbumInfo)
}
I have added several other utility functions to Zousan-Plus:
Similar to the standard Promise.all except each promise in the list is identified with a name and the eventual object that is returned has name/value pairs for each promise rather than an array.
return Zousan.namedAll({
id: userId, // Integer
pb: startProgressBar, // function whose return is ignored
user: getUser(userId), // returns a promise
items: getUserItemList(userId) // returns a promise
})
.then(function(ob) {
// Here ob contains the following:
// { id: userId, pb: <??>, user: userObject <from resolved promise>, items: itemList <from promise> }
endProgressBar()
})
This is useful for taking a traditional callback-based module and turning each member function into promises. Node users know what I'm talking about:
const fs = Zousan.promisify(require("fs"))
fs.readFile("path-to-file.txt", { encoding: "utf-8" })
.then(function(contents) {
// you have the file contents here!
})
Simple syntax sugar for expressing a list of functions to call in a series. This is expresses a function composition with each function being passed the eventual result of the previous promise in the series.
function getUserAlbumCovers(userId)
{
return Zousan.series(
userId,
getUserObj,
prop("albumList"), // extracts "albumList" property
getAlbumsByIDList,
pluck("id"), // extracts list of "id" from list of obj
getAlbumCoversByIDList)
}
This is similar to the series function above, but the values resolved at each step in the series are tracked and available when the full series resolves:
function add6(x) { return x + 6 }
function add3Later(x) { // Return the specified value plus 3 after 100ms
/* ... */ }
var ts = tSeries(1,add6,add3Later) // ts has the "res" and "prom" properties
ts.prom.then(function(final) {
// ts.res[0] = 1
// ts.res[1] = 9
// ts.res[2] = 12
// final = 12
})
This library is sure to grow with more useful function for dealing effectively with asynchronicity. These are all contained in a single module, but each is independent (except series depends on tSeries) so they can be copies and pasted if you just want a single function.
These are written against Zousan - but will work with any Promise A+ compliant promise implementation, such as the native Promise contained in evergreen browsers. Simply change any instance of Zousan to Promise (or assign Zousan = Promise somewhere above it).
Contributions are certainly welcome