// TODO: talk about tests, under development
"Promisified" Array.
Prray aims to replace original Array in some cases for convenience. Its methods comes with promise support, that is to say you can call method map
with async function as parameter to perform ideal effect. And it's compatible with original Array as far as possible.
import { prray } from 'prray'
(async () => {
// Convert original array to "prray"
const prr = prray(['www.google.com', 'npmjs.org'])
// Now you can do something like this
const responses = await prr.map(fetch)
// Method chaining with async function works well just like with common function.
const htmls = await prr.map(fetch).map(r => r.text())
// You don't even have to distinguish between async function and common function.
await prr.filter(asyncFunc).sort(commonFunc).reduce(asyncFunc2)
})()
npm
npm install prray --save
yarn
yarn add prray
import { prray, Prray } from 'prray'
const arr = [1,2,3]
const prr = prray(arr)
prr[0] // 1
prr.length // 4
[...prr] // [1,2,3]
prr instanceof Array // true
Array.isArray(prr) // true
JSON.stringify(prr) // "[1, 2, 3]"
prr instanceof Prray // true
Prray is "almost" compatible with original Array, but it also has differences. As features, the way some methods works are actually quite different with original.
For example, let's talk about method map
. If calling it with async mapper, it will returns an array of promise in original Array, but returns a promise of array(prray actually) in Prray. And it will alway returns a promise whenever the mapper is async or not.
The details of each method are below, and methods which different with original is marked with *
Method prray
convert a normal array to prray.
import { prray } from 'prray'
const prr = prray([1,2,3])
Class Prray
. You can think of it as Array
.
import { Prray } from 'prray'
new Prray() // think of new Array()
new Prray(1) // think of new Array(1)
new Prray('a', 'b') // think of new Array('a', 'b')
The map
method returns promise of a new array with the return values or the resolved values of return promises of calling a provided callback on every element. You can think of it as an async version of Array.prototype.map
.
callback(currentValue, index, prray)
// With async callback
const resps = await prr.map(fetch)
// With common callback
const nums = await prr.map(v => v + 1)
// Method chaining
const jsons = await prr.map(fetch).map(res => res.json())
The filter
method returns promise of a new array with all elements that pass the test implemented by the provided function. You can think of it as an async version of Array.prototype.filter
.
// With async callback
const existedFiles = await prr.filter(isFileExisted)
// With common callback
const evenNums = await prr.filter(v => v % 2 === 0)
// Method chaining
await prr.filter(isFileExisted).map(removeFile)
You also can chain method calls even if them returns promise 😻!
Different from Bluebird
First, prray does not provide another implementation of promise, which is essentially different from Bluebird.
Secondly, prray aims to provide a better way to handle asynchronous batch operations on data(array). In this aspect, maybe you work well with Bluebird's methods such as all
and map
, but prray gives you another option more appropriate in some cases.
const urls = [ /* some urls */ ]
// use prray
await p(urls).mapAsync(fetch)
.filterAsync(isExisted)
.mapAsync(saveAsync)
// use bluebird
await Bluebird.mapAsync(await Bluebird.filter(await Bluebird.map(urls, fetch), isExisted), saveAsync)
// use bluebird and prettier
let responses = await Bluebird.map(urls, fetch)
responses = await Bluebird.filter(responses, isExisted)
await Bluebird.map(responses, saveAsync)
If you want a good promise implementation, this is bluebird.
If you want to handle asynchronous batch operations on data(array), prray is an option for you.
npm:
npm install prray --save
yarn:
yarn add prray
Get a prray from existing array using function p
.
const p = require('prray')
const arr = [1,2,3]
const prray = p(arr)
prray.mapAsync(funcAsync).then(console.log)
- Prray#mapAsync -> Array#map
- Prray#filterAsync -> Array#filter
- Prray#reduceAsync -> Array#reduce
- Prray#everyAsync -> Array#every
- Prray#someAsync -> Array#some
- Prray#findAsync -> Array#find
- Prray#findIndexAsync -> Array#findIndex
- Prray#toArray
await p(arr).filterAsync(existAsync).mapAsync(postAsync)
// equals to:
let existed = await p(arr).filterAsync(existAsync)
await existed.mapAsync(postAsync)
an async version of Array#map
await p(urls).mapAsync(async (url, ix) => {
const res = await fetch(url)
return res.json()
})
an async version of Array#filter
await p(filenames).filterAsync(async (filename, ix) => {
return existsAsync(filename)
})
an async version of Array#reduce
await p(userIds).reduceAsync(async (sum, uid, ix) => {
const score = await getScoreFromDB(uid)
return sum + score
}, 0)
an async version of Array#every
const areVip = await p(users).everyAsync(async (user, ix) => {
return await userModel.isVip(user)
})
console.log(areVip) // true
an async version of Array#some
const hasVipUser = await p(users).someAsync(async (user, ix) => {
return await userModel.isVip(user)
})
console.log(areVip) // true
an async version of Array#find
const vipUser = await p(users).findAsync(isVipAsync)
an async version of Array#findIndex
const ix = await p(users).findIndexAsync(isVipAsync)
const vipUser = users[ix]
Such as map
, filter
, indexOf
, lastIndexOf
...
You may optionally specify a concurrency limit when calling async method. It is useful when perform some resource-consuming operations in large batches, such as querying the database.
await p(urls).mapAsync(fetch, 10) // concurrency limit 10
NOTE: method
reduceAsync
does NOT support concurrency limit, its concurrency is alway 1.
const prr = p([1,2,3,4])
Array.isArray(prr) // true
prr instanceof Array // true
JSON.stringify(prr) === JSON.stringify([1,2,3,4]) // true
console.log(prr.map) // [Function]
console.log(prr.length) // 4
console.log(prr.mapAsync) // [Function]
In some cases you may need a 'pure' array, just call method toArray
.
const p = p([1,2,3])
console.log(p.toArray()) // [1,2,3]
- Concurrency
- A well-designed logo
- Sub-task promise supports
timeout
, such likeawait prr.mapAsync(fetch, {timeout: 3000})
- Sub-task promise supports
retry
when rejected, such likeawait prr.mapAsync(fetch, {retries: 2})
- Browser compatibility survey
- Prettier document
- Revise documentation, including syntax errors
- ......