0.0.3 • Published 4 years ago

aaarray v0.0.3

Weekly downloads
9
License
MIT
Repository
github
Last release
4 years ago

aaarray

Typed Async / Await Arrays

Github ActionsCoverage Status Maintainability

Have you ever wanted to write something like this?

await [1, 2, 3]
.map(n => (await fetch(`https://example.com/${n}`)).json())
.filter(r => checkAsync(r));

Well you still kind of can but it's not... pretty.

Promise.all([1, 2, 3]
.map(n => fetch(`https://example.com/${n}`)))
.then(results => Promise.all(results.map(res => res.json())))
.then(results => Promise.all(results.map(r => checkAsync(r))))
.then(results => results.filter(Boolean));

What if you also wanted to reduce, or forEach, or flat, or any other array method? It can get pretty complicated with a lot of boiler plate to make sure that your async array operations run how you expect.

This is where AAArray comes in - with a single wrapper function you can create an array that handles both async and sync callbacks in the exact same manner as normal arrays, completely chainable too!

import AA from "aaarray";

await AA([1, 2, 3])
.map(n => (await fetch(`https://example.com/${n}`).json()))
.filter(r => checkAsync(r));

How?

AAArray keeps an internal array and a queue of actions to run when told to resolve. AAArray is a PromiseLike that will resolve with a normal, ie. non-AAArray, value.

There is a neat syntactical trick that is used here: await will automatically put in a call to then thus allowing us to leave off any "promisy" looking syntax. Of course if you are in an environment where async/await is not available just use then as you would on a normal Promise to get the result of the AAArray.

(Im)mutable

The internal array that an AAArray instance uses to keep the running valuation is immutable in the sense that it is reassigned with each stage of the chain. For all intents and purposes consider AAArray instances as immutable.

However many of the methods in the Array API can directly mutate or otherwise alter the array while still returning something else. For example pop() will alter the array by removing the first element but the method also returns that first element.

Serial Alternatives

Introducing async support to some array methods meant that the iterator could be run in parallel. However, there may be instances where a developer wants to wait for each iteration to finish in order before proceeding to the next one. Each method therefore has a serial alternative, suffixed with Serial, that performs the operation in serial instead.

Note that reduce and reduceRight are serial only methods as a parallel accumulator is a horrid sepulcra of race conditions.

Additional Methods

While the full Array API is present I have also added some additional methods that helped fill the gap between the synchronous API and the chainable async nature of AAArray.

  • get
  • each
  • eachSerial

Typescript

This project is written in Typescript and will correctly type each stage in a chain. For example if you run a map that changes the type this will be reflected immediately in the next callback you use in the chain.

await AA([1, 2, 3])
.map((n: number) => n.toString())
.forEach((s: string) => console.log(s));

If you encounter any issues with this please let me know as I find the type system fascinating and will forsake all other issues to investigate it.

In addition, Typescript complains about directly bound methods being passed as callbacks to normal Array methods that don't fully satisfy the callback API expected - for example if you wanted to log each item in a forEach with console.log.bind(console). This is possible with AAArray for no other reason than because I wanted it.