npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@asn.aeb/async-array

v1.5.9

Published

Array class with non blocking async methods and await support

Downloads

88

Readme

AsyncArray

npm deps license

Arrays with async capabilities

Array class extension that uses setImmediate on iterative methods so that the event loop won't be stuck until the end of the iteration. Additionally, async callbacks can be used on every instance method that takes a callback as an argument.

Keep in mind that async methods are slower than native ones so use it when speed is not a priority while not blocking the event loop is.

Installation

npm i @asn.aeb/async-array

Usage

import {AsyncArray} from '@asn.aeb/async-array'

An instance of AsyncArray can be constructed with normal class syntax and its constructor shares the same signature with Array's constructor.

let arr

arr = new AsyncArray()
// -> AsyncArray(0) []

arr = new AsyncArray(3) 
// -> AsyncArray(3) [<empty>, <empty>, <empty>]

arr = new AsyncArray(1, 2, 3) 
// -> AsyncArray(3) [1, 2, 3]

It works just like a normal array

arr.push(4, 5, 6)

arr[6] = 7

for (const item of arr) { /* .. */ }

With one additional property on its prototype

arr.async

Under this property, beside this library's own methods, you will find many methods that have the same name as Array.prototype methods that you know. They will provide exactly the same functionality with the difference that they will run asynchronously, not blocking the Node.js event loop, and will return a promise. Just append await before the method invocation or use .then after it and use it like you normally would. Some examples can be:

await arr.async.filter(item => item > 5) 
// -> AsyncArray(2) [6, 7] 

await arr.async.find(item => item > 5) 
// -> 6 

await arr.async.reduce((acc, curr) => `${acc}.${curr}`) 
// -> '1.2.3.4.5.6.7'

await arr.async.some(item => item > 10) 
// -> false

await arr.async.map(item => item * 10) 
// -> AsyncArray(7) [10, 20, 30, 40, 50, 60, 70]

AsyncArray.prototype.async

  • every
  • filter
  • fill
  • find
  • findIndex
  • findLast
  • findLastIndex
  • forEach
  • includes
  • indexOf
  • lastIndexOf
  • map
  • reduce
  • reduceRight
  • some
  • splitToChunks

Derive from existing arrays

You may find yourself wanting to use these methods on an existing Array object. Maybe you fetched some data from somewhere - or got a computation result from a library - in the form of an array

const myArray = ['normal', 'boring', 'array']

Async methods can be used on this object in two ways:

1. Copy the array into a new AsyncArray object

Using the from static method, myArray will be left untouched and a new object will be created in the form of an AsyncArray instance

const myAsyncArray = AsyncArray.from(myArray)

myAsyncArray[0] = 'async'
myAsyncArray[1] = 'fun'

console.log(myArray)
// prints -> (3) ['normal', 'boring', 'array']

console.log(myAsyncArray)
// prints -> AsyncArray(3) ['async', 'fun', 'array']

console.log(myArray === myAsyncArray)
// prints -> false

console.log(myArray instanceof AsyncArray)
// prints -> false

Here, myAsyncArray and myArray will reference two different objects.

2. Transform the Array to an AsyncArray

Copying over a very large array to a new object can take a while and can be often unnecessary, remember we are here to avoid blocking. You can transform an array-like object to an AsyncArray by using the to static method. Let's repeat the previous example

const myAsyncArray = AsyncArray.to(myArray)

myAsyncArray[0] = 'async'
myAsyncArray[1] = 'fun'

console.log(myArray)
// prints -> AsyncArray(3) ['async', 'fun', 'array']

console.log(myAsyncArray)
// prints -> AsyncArray(3) ['async', 'fun', 'array']

console.log(myArray === myAsyncArray)
// prints -> true

console.log(myArray instanceof AsyncArray)
// prints -> true

As you can see, myArray has not been duplicated. It has just been transformed to an AsyncArray instance and variable myAsyncArray now just references that object. Note that this will not mutate your array which will still be assignable to whatever it was before. All this will do is adding async with its methods to the object properties.

Typescript users note

If you are looking carefully, you will notice the above example could have been written as follows

AsyncArray.to(myArray)

myArray[0] = 'async'
myArray[1] = 'fun'

console.log(myArray instanceof AsyncArray) // -> true

While this is generally okay, it is always better to assign the transformed object to a new identifier when using Typescript so that you can have the correct type on it. Otherwise, you may incurr in problems with typings.

Async Callbacks

Iterating methods are passed a callback function to be executed on each item of the array they are iterating. Callbacks passed to AsyncArray methods can return a Promise which will be resolved before the next iteration. This allows the use of async callbacks or callbacks that just return a promise which will be automatically resolved.

Examples

Log an item every second

const myArray = new AsyncArray(1, 2, 3)

// Returned promise is automatically resolved before next iteration
myArray.async.forEach(item => new Promise(resolve => {
    setTimeout(() => resolve(console.log(item)), 1000)
}))

Sequentially query a database with key-schema {id: number} from an array of numbers and retrieve the result into a new AsyncArray

const keys = new AsyncArray(1, 2, 3)

const db = myDbClient() // <- Hypotetical database client

// Assume db.get() returns a promise that resolves to the actual item
const items = await arr.async.map(id => db.get({id}))

Note on map

When using async.map method, you may want to obtain an array of promises rather than resolving them sequentially. To this purpose, async.map method can be given an additional argument, specifying whether or not the promises returned should be resolved. Defaults to true if omitted

const promises = await arr.async.map(id => db.get({id}), false)

// Promises can now be resolved in parallel
const items = await Promise.all(promises)

Own methods

The following methods are not derived from Array.prototype methods and provide additional functionalities, always in the same async flavor.

splitToChunks

Splits the AsyncArray object into AsyncArray's of the size passed to its parameter. The last chunk may have a shorter size.

const whole = new AsyncArray(1, 2, 3, 4, 5, 6, 7)
const split = await whole.async.splitToCunks(2)

console.log(split.length)
// prints -> 4

console.log(split[0])
// prints -> AsyncArray(2) [1, 2]

console.log(split[1])
// prints -> AsyncArray(2) [3, 4]

console.log(split[2])
// prints -> AsyncArray(2) [5, 6]

console.log(split[3])
// prints -> AsyncArray(2) [7]

Info

This library is under development. More methods are planned to be added in the future and some work is scheduled to investigate how the speed can be improved. While it can be useful, keep in mind that this is not complete and its api is heavily subject to changes. Contribution / suggestions / feedback on GitHub are really appreciated.

Typescript known issues

Due to typescript issue #51993, when using AsyncArray with Promise.all or Promise.allSettled, object must be cast to a normal array or typings will be broken

const promiseArray = new AsyncArray(Promise.resolve(0), Promise.resolve(1))

Promise.all(promiseArray as Promise<number>[])
Promise.allSettled(romiseArray as Promise<number>[])