types.js
Library of JavaScript type extensions, types and utilities.
-
types.js
- Installation
- Basic usage
-
Object
-
Function
-
Array
<array>.first(..)
/<array>.last(..)
<array>.rol(..)
<array>.compact()
<array>.len
<array>.unique()
/<array>.tailUnique()
<array>.trim()
/<array>.trimStart()
/<array>.trimEnd()
<array>.cmp(..)
<array>.setCmp(..)
<array>.sortAs(..)
<array>.inplaceSortAs(..)
<array>.toKeys(..)
<array>.toMap(..)
Array.zip(..)
/<array>.zip(..)
Array.iter(..)
/<array>.iter()
<array>.between(..)
-
Abortable
Array
iteration -
Large
Array
iteration (chunked)
-
Map
-
Set
-
Date
-
String
-
RegExp
-
Promise
- Interactive promises
- Cooperative promises
-
Promise iteration
Promise.iter(..)
/promise.IterablePromise(..)
<promise>.iter()
<promise-iter>.iter()
Promise.seqiter(..)
/promise.IterableSequentialPromise(..)
<promise>.seqiter()
/<promise-iter>.seqiter()
Promise.seqstartiter(..)
/promise.IterableSequentialStartPromise(..)
<promise>.seqstartiter()
/<promise-iter>.seqstartiter()
<promise-iter>.map(..)
/<promise-iter>.filter(..)
/<promise-iter>.reduce(..)
<promise-iter>.between(..)
<promise-iter>.flat(..)
<promise-iter>.reverse()
<promise-iter>.concat(..)
<promise-iter>.push(..)
/<promise-iter>.unshift(..)
<promise-iter>.at(..)
/<promise-iter>.first()
/<promise-iter>.last()
<promise-iter>.join(..)
<promise-iter>.some(..)
/<promise-iter>.find(..)
- Array proxy methods returning
<promise-iter>
- Array proxy methods returning a
<promise>
<promise-iter>.then(..)
/<promise-iter>.catch(..)
/<promise-iter>.finally(..)
<promise-iter>.iterthen(..)
<promise-iter>.isSync(..)
<promise-iter>.sync(..)
promise.IterablePromise.STOP
/promise.IterablePromise.STOP(..)
- Advanced handler
- Stopping the iteration
- Promise proxies
- Sync/async promise
- Promise utilities
-
Generator extensions and utilities
- The basics
-
Generator instance iteration
<generator>.iter(..)
<generator>.map(..)
/<generator>.filter(..)
<generator>.reduce(..)
/<generator>.greduce(..)
<generator>.forEach(..)
(EXPERIMENTAL)<generator>.between(..)
<generator>.slice(..)
<generator>.at(..)
/<generator>.gat(..)
<generator>.flat(..)
<generator>.shift()
/<generator>.pop()
/<generator>.gshift()
/<generator>.gpop()
<generator>.unshift(..)
/<generator>.push(..)
<generator>.join(..)
<generator>.unwind(..)
<generator>.then(..)
/<generator>.catch(..)
/<generator>.finally(..)
<generator>.toArray()
- Treating iterators the same as generators
-
Generator constructor iteration
<Generator>.iter(..)
<Generator>.at(..)
/<Generator>.gat(..)
<Generator>.shift()
/<Generator>.pop()
/<Generator>.gshift()
/<Generator>.gpop()
<generator>.unshift(..)
/<generator>.push(..)
<Generator>.slice(..)
<Generator>.map(..)
/<Generator>.filter(..)
/<Generator>.reduce(..)
/<Generator>.flat()
<Generator>.between(..)
<Generator>.toArray()
<Generator>.join(..)
<Generator>.unwind(..)
- Generator combinators
- Generator library
- Generator helpers
-
Async generator extensions
generator.AsyncGenerator
<async-generator>.unwind(..)
<async-generator>.then(..)
/<async-generator>.catch(..)
/<async-generator>.finally(..)
<async-generator>.iter(..)
<async-generator>.map(..)
/<async-generator>.filter(..)
/<async-generator>.reduce(..)
<async-generator>.chain(..)
<async-generator>.flat(..)
<async-generator>.concat(..)
<async-generator>.push(..)
/<async-generator>.unshift(..)
-
Containers
-
containers.UniqueKeyMap()
(Map
)<unique-key-map>.set(..)
<unique-key-map>.reset(..)
<unique-key-map>.rename(..)
<unique-key-map>.orderedRename(..)
<unique-key-map>.unorderedRename(..)
<unique-key-map>.keysOf(..)
<unique-key-map>.originalKey(..)
<unique-key-map>.uniqueKey(..)
<unique-key-map>.__key_pattern__
<unique-key-map>.__unordered_rename__
-
- Event
-
Runner
-
Micro task queue
runner.STOP
runner.SKIP
Queue(..)
/Queue.runTasks(..)
Queue.handle(..)
<queue>.state
<queue>.start(..)
<queue>.stop(..)
<queue>.runTask(..)
<queue>.tasksAdded(..)
(event)<queue>.taskStarting(..)
(event)<queue>.taskFailed(..)
(event)<queue>.taskCompleted(..)
(event)<queue>.queueEmpty(..)
(event)<queue>.prioritize(..)
<queue>.delay(..)
<queue>.add(..)
<queue>.clear(..)
FinalizableQueue(..)
/FinalizableQueue.runTasks(..)
(Queue)<finalizable-queue>.done(..)
(event/method)<finalizable-queue>.abort(..)
(event/method)<finalizable-queue>.promise(..)
<finalizable-queue>.then(..)
<finalizable-queue>.catch(..)
-
Large task management
runner.TaskManager(..)
<task-manager>.Task(..)
<task-manager>.sync_start
<task-manager>.record_times
<task-manager>.titled(..)
<task-manager>.send(..)
<task-manager>.stop(..)
<task-manager>.done(..)
(event)<task-manager>.error(..)
(event)<task-manager>.tasksDone(..)
(event)runner.TaskTicket(..)
runner.TaskMixin(..)
-
Micro task queue
- License
Installation
$ npm install -s 'ig-types'
Basic usage
To extend everything:
require('ig-types')
To have access to additional library types and utilities:
var types = require('ig-types')
types.js
is organized so as to be able to import/extend only specific
sub-modules mostly independently so...
In case there is a need to only extend a specific constructor just import
the module dealing with that constructor (Array
in this case):
// require `ig-types/<constructor-name>`...
require('ig-types/Array')
Note that type patching modules are mostly independent.
And to import specific library modules only:
var containers = require('ig-types/containers')
Object
require('ig-types/Object')
Note that this module imports from
object.js
and
object-run.js
,
see those modules for more details.
Object.deepKeys(..)
Get list of keys from all objects in the prototype chain.
Object.deepKeys(<obj>)
-> <keys>
This is different from Object.keys(..)
which only gets own keys from the
current object.
Example:
var a = { x: 123 }
var b = Object.create(a)
b.y = 321
// get own keys of b...
Object.keys(b) // -> ['y']
// get all keys accessible from b...
Object.deepKeys(b) // -> ['x', 'y']
For more details see: https://github.com/flynx/object.js#deepkeys
Object.copy(..)
(EXPERIMENTAL)
Create a copy of <obj>
Object.copy(<obj>)
-> <obj-copy>
Object.copy(<obj>, <constructor>)
-> <obj-copy>
This will:
- create a blank
<obj-copy>
- link
<obj-copy>
to the same prototype chain - assign all own keys from
<obj>
to<obj-copy>
This is similar to Object.clone(..)
but instead of creating a new descendant of
the input object with no data this will instead create a new sibling with a copy
of the instance data.
<constructor>
if given is called to create the instance to be populated,
otherwise Object.create(<obj>)
is used.
Note that .assign(..)
is used to copy data, thus properties will be copied as values, to copy instance properties use object.js
's
.mixinFlat(..)
.
Note that this will make no attempt to clone object type, a <constructor>
should be passed manually if any instance type other that Object
is required.
Object.flatCopy(..)
Copy all attributes from the prototype chain of <obj>
into <new-obj>
.
Object.flatCopy(<obj>)
-> <new-obj>
Object.flatCopy(<obj>, <constructor>)
-> <new-obj>
This is different to .copy(..)
in that if
no <constructor>
is given <new-obj>
will not be linked into the
prototype chain of <obj>
, if this behavior is desired use o => Object.create(o)
as the <constructor>
.
Object.match(..)
Attribute/value match two objects (non-recursive).
Object.match(<object>, <other>)
-> <bool>
Objects A
and B
match iff:
-
A
andB
are identical, i.e.A === B
or
-
typeof A == typeof B
and, -
A
andB
have the same number of attributes and, - attribute names match and,
- attribute values are identical.
And for a less strict match:
Object.match(<object>, <other>, true)
-> <bool>
Like the default case but uses equality instead of identity to match values.
For more details see: https://github.com/flynx/object.js#match
Object.matchPartial(..)
Object.matchPartial(<object>, <other>)
-> <bool>
Object.matchPartial(<object>, <other>, true)
-> <bool>
Like .match(..)
but will check for a partial match, i.e. when <other>
is a non-strict subset of <object>
.
For more details see: https://github.com/flynx/object.js#matchpartial
<object>.run(..)
<object>.run(<func>)
-> <object>
-> <other>
Run a function in the context of <object>
returning either <object>
itself (if returning undefined
) or the result.
Note that this is accessible from all JavaScript non-primitive objects,
i.e. everything that inherits from Object
.
Example:
var L = [1, 2, 3]
.map(function(e){
return e * 2 })
// see if the first element is 1 and prepend 1 if it is not...
.run(function(){
if(this[0] != 1){
this.unshift(1) } })
console.log(L) // -> [1, 2, 6, 8]
.run(..)
is also available standalone via:
$ npm install -s object-run
For more details see:
https://github.com/flynx/object-run.js
Object.sort(..)
Sort <obj>
attributes (similar to Array
's .sort(..)
)
Object.sort(<obj>)
-> <obj>
Sort <obj>
attributes via <cmp>
function.
Object.sort(<obj>, <cmp>)
-> <obj>
Sort <obj>
attributes to the same order of <order-list>
.
Object.sort(<obj>, <order-list>)
-> <obj>
Note that this rewrites all the keys of <obj>
thus for very large
sets of keys/attributes this may be quite expensive.
Note that some keys of Object
may misbehave in JavaScript, currently keys
that are string values of numbers are sorted automatically by number value
and are not affected by .sort(..)
, this affects both Chrome and Firefox.
Example:
var o = {x: 0, a: 1, '100':2, '0':3, ' 27 ':4, b:5}
// notice that the order is already different to the order of attributes above...
Object.keys(o)
// -> ['0', '100', 'x', 'a', ' 27 ', 'b']
// '0' and '100' are not affected by .sort(..) while ' 27 ' is...
Object.keys(Object.sort(o, ['x', 'a', '100']))
// -> [ '0', '100', 'x', 'a', ' 27 ', 'b' ]
This is similar to <map>.sort(..)
and <ser>.sort(..)
.
Function
var func = require('ig-types/Function')
func.AsyncFunction
The async function constructor.
This enables us to test if an object is an instance of an async function.
var a = async function(){
// ...
}
a instanceof func.AsyncFunction // -> true
This is hidden by JavaScript by default.
Array
require('ig-types/Array')
or
var array = require('ig-types/Array')
<array>.first(..)
/ <array>.last(..)
Get the first/last items of <array>
.
<array>.first()
-> <item>
<array>.last()
-> <item>
Set the first/last items of <array>
.
<array>.first(<item>)
-> <array>
<array>.last(<item>)
-> <array>
Note that these do not affect <array>
length unless setting items on
an empty <array>
.
<array>.rol(..)
Roll <array>
in-place left.
<array>.rol()
<array>.rol(1)
-> <array>
<array>.rol(n)
-> <array>
To roll right pass a negative n
to .rol(..)
.
<array>.compact()
<array>.compact()
-> <compact-array>
Generate a compact <array>
from a sparse <array>
, i.e. removing all
the empty slots.
<array>.len
Number of non-empty slots/elements in <array>
.
This is similar to:
var L = [,,, 1,, 2, 3,,]
// this is the same as L.len...
L.compact().length
Note that this is different from .length
in that writing to .len
has
no effect.
<array>.unique()
/ <array>.tailUnique()
Generate an array with all duplicate elements removed.
<array>.unique()
-> <array>
<array>.tailUnique()
-> <array>
The difference between the two versions is in that .unique(..)
keeps the
first occurrence of a value while .tailUnique(..)
keeps the last.
<array>.trim()
/ <array>.trimStart()
/ <array>.trimEnd()
Copy array removing empty slots from array start, end or both.
<array>.trim()
-> <array>
<array>.trimStart()
-> <array>
<array>.trimEnd()
-> <array>
This is similar to String
's equivalent methods but removing empty slots
instead of spaces.
<array>.cmp(..)
Compare two arrays.
<array>.cmp(<other>)
-> <bool>
This will return true
if:
<array> === <other>
or
- lengths are the same and,
- values on the same positions are equal.
<array>.setCmp(..)
Compare to arrays ignoring element order and count.
<array>.setCmp(<other>)
-> <bool>
<array>.sortAs(..)
Sort array as a different array.
<array>.sortAs(<other>)
<array>.sortAs(<other>, 'head')
-> <array>
<array>.sortAs(<other>, 'tail')
-> <array>
Elements not present in <other>
retain their relative order and are
placed after the sorted elements if 'head'
(i.e. "sorted at head of
array") is passed as second argument (default) and before them if
'tail'
("sorted at tail") is passed.
Example:
var L = [1, 2, 3, 4, 5, 6]
var O = [5, 3, 1, 0]
L.sortAs(O) // -> [5, 3, 1, 2, 4, 6]
<array>.inplaceSortAs(..)
Sort array as a different array keeping positions of unsorted elements.
<array>.inplaceSortAs(<other>)
-> <array>
Example:
var L = [1, 2, 3, 4, 5, 6]
var O = [5, 3, 1, 0]
L.inplaceSortAs(O) // -> [5, 2, 3, 4, 1, 6]
<array>.toKeys(..)
Create an object with array values as keys and index as value.
<array>.toKeys()
-> <object>
Normalize resulting <object>
keys:
<array>.toKeys(<normalize>)
-> <object>
<normalize>(<elem>, <index>)
-> <key>
If <array>
contains the same value multiple times it will be written
to <object>
only once with the last occurrences' index.
Since object
keys can only be string
s array items that are not
strings will be converted to strings. If this is not desired use .toMap(..)
instead.
<array>.toMap(..)
Create a map with array values as keys and index as value.
<array>.toMap()
-> <map>
Normalize resulting <map>
keys:
<array>.toMap(<normalize>)
-> <map>
<normalize>(<elem>, <index>)
-> <key>
Note that if <array>
contains the same value multiple times it will be used
as key only once and retain the last occurrences' index.
Array.zip(..)
/ <array>.zip(..)
Zip input array items.
Array.zip(<array>, <array>, ..)
-> <array>
<array>.zip(<array>, <array>, ..)
-> <array>
Example:
var A = [1, 2, 3]
var B = ['a', 'b', 'c', 'd']
Array.zip(A, B) // -> [[1, 'a'], [2, 'b'], [3, 'c'], [, 'd']]
Array sparseness is retained -- if one of the arrays has an empty slot, or is not long enough, the corresponding spot in the result will be empty.
Resulting array length is strictly equal to the longest input array length.
Array.iter(..)
/ <array>.iter()
Return an iterator/generator from the current array.
<array>.iter()
<array>.iter(<func>)
<array>.iter(<func>, <onstop>)
-> <generator>
<func>(<elem>, <index>)
-> <value>
<onstop>(STOP, ...<args>)
<onstop>(<value>, ...<args>)
This is mostly useful in combination with the Generator extensions and utilities
Note that all stoppable functions/iterators support <onstop>
callback as
the last argument.
<array>.between(..)
<array>.between(<value>)
-> <array>
<array>.between(<func>)
-> <array>
<func>([<pre>, <post>], <index-in>, <index-out>, <array>)
-> <value>
Array
iteration
Abortable A an alternative to Array
's .map(..)
/ .filter(..)
/ .. methods with ability to
stop the iteration process by throw
ing STOP
or STOP(<value>)
.
var {STOP} = require('ig-types/Array')
This can be used in two ways:
-
throw
as-is to simply stop...;[1,2,3,4,5] .smap(function(e){ // simply abort here and now... throw STOP })
Since we aborted the iteration without passing any arguments to
STOP
,.smap(..)
will returnundefined
. -
throw
an instance and return the argument...// this will print "4" -- the value passed to STOP... console.log([1,2,3,4,5] .smap(function(e){ if(e > 3){ // NOTE: new is optional here... // ...StopIteratiom is an object.js constructor. throw new STOP(e) } }))
Note that no partial result is returned unless passed through STOP(..)
.
array.STOP
/ array.STOP(..)
An object/constructor that if raised (as an exception) while iterating via a supporting iterator method will abort further execution and correctly exit.
<array>.smap(..)
/ <array>.sfilter(..)
/ <array>.sreduce(..)
/ <array>.sforEach(..)
Like Array
's .map(..)
, .filter(..)
, .reduce(..)
and .forEach(..)
but
with added support for aborting iteration by throwing STOP
or STOP(<value>)
.
These can be passed a <onstop>
callback as an additional last argument
that will be called if STOP
/STOP(<value>)
is returned or thrown.
Array
iteration (chunked)
Large Iterating over very large Array
instances in JavaScript can block execution,
to avoid this types.js
implements .map(..)
/.filter(..)
/.reduce(..)
equivalent methods that iterate the array in chunks and do it asynchronously
giving the runtime a chance to run in between.
In the simplest cases these are almost a drop-in replacements for the equivalent methods but return a promise.
var a = [1,2,3,4,5]
.map(function(e){
return e*2 })
var b
;[1,2,3,4,5]
.mapChunks(function(e){
return e*2 })
.then(function(res){
b = res })
// or with await...
var c = await [1,2,3,4,5]
.mapChunks(function(e){
return e*2 })
These support setting the chunk size (default: 50
) as the first argument:
var c = await [1,2,3,4,5]
.mapChunks(2, function(e){
return e*2 })
array.STOP
/ array.STOP(..)
Like for <array>.smap(..)
and friends iteration
can be stopped by throwing a array.STOP
/ array.STOP(<value>)
and as before
there are two ways to go:
-
throw
as-is to simply stop;[1,2,3,4,5] .mapChunks(function(e){ // simply abort here and now... throw STOP }) .catch(function(){ console.log('done.') })
-
Throw
an instance and pass a value to.catch(..)
;[1,2,3,4,5] .mapChunks(function(e){ if(e > 3){ // NOTE: new is optional here... // ...StopIteratiom is an object.js constructor. throw new STOP(e) } }) .catch(function(e){ console.log('first value greater than 3:', e) })
<array>.CHUNK_SIZE
The default iteration chunk size.
Note that the smaller this is the more responsive the code is, especially in UI applications but there is a small overhead added per chunk.
Default value: 50
<array>.mapChunks(..)
/ <array>.filterChunks(..)
/ <array>.reduceChunks(..)
The .map(..)
, .filter(..)
and .reduce(..)
alternatives respectively:
<array>.mapChunks(<func>)
<array>.mapChunks(<chunk-size>, <func>)
-> <promise>
<func>(<item>, <index>, <array>)
-> <new-item>
<array>.filterChunks(<func>)
<array>.filterChunks(<chunk-size>, <func>)
-> <promise>
<func>(<item>, <index>, <array>)
-> <bool>
<array>.reduceChunks(<func>, <state>)
<array>.mreduceChunks(<chunk-size>, <func>, <state>)
-> <promise>
<func>(<state>, <item>, <index>, <array>)
-> <state>
All three support chunk handlers in the same way (illustrated on .mapChunks(..)
):
<array>.mapChunks([<func>, <chunk-handler>])
<array>.mapChunks(<chunk-size>, [<func>, <chunk-handler>])
-> <promise>
<func>(<item>, <index>, <array>)
-> <new-item>
<chunk-handler>(<chunk>, <result>, <offset>)
The <chunk-handler>
gets the completed chunk of data after it is computed
but before the timeout.
Map
require('ig-types/Map')
<map>.replaceKey(..)
Replace key in map retaining item order
<map>.replaceKey(<old>, <new>)
<map>.replaceKey(<old>, <new>, true)
-> <map>
Replace the key without sorting
<map>.replaceKey(<old>, <new>, false)
-> <map>
Note that when sorting large maps this can get expensive.
<map>.sort(..)
Sort <map>
keys in-place
<map>.sort()
-> <map>
<map>.sort(<cmp>)
-> <map>
In the general case this is similar to
<array>.sort(..)
with the addition of the <array>.sortAs(..)
's ability to sort
as a list
<map>.sort(<sorted-keys>)
-> <map>
This is similar to <set>.sort(..)
and Object.sort(..)
,
see the later for more info.
Set
require('ig-types/Set')
<set>.unite(..)
Unite two sets and return the resulting set
<set>.unite(<other>)
-> <union-set>
This is a shorthand for new Set([...<set>, ...<other>])
<set>.intersect(..)
Intersect two sets and return the intersection set
<set>.untersect(<other>)
-> <intersection-set>
<set>.subtract(..)
Subtract <other>
from set and return resulting set
<set>.subtract(<other>)
-> <sub-set>
<set>.splice(..)
In-place splice a set
<set>.splice(<from>)
<set>.splice(<from>, <count>)
<set>.splice(<from>, <count>, ...<items>)
-> <removed>
This is the same as
<array>.splice(..)
but without the ability to add more than one instance of an item.
<set>.replace(..)
Replace value in set with other value retaining item order (in-place)
<set>.replace(<old>, <new>)
<set>.replace(<old>, <new>, true)
-> <set>
Replace the value without sorting
<set>.replace(<old>, <new>, false)
-> <set>
Note that when sorting large sets this can get expensive.
<set>.replaceAt(..)
Replace item at position in set retaining order (in-place)
<set>.replaceAt(<index>, <new>)
<set>.replaceAt(<index>, <new>, true)
-> <set>
If <index>
is less than 0
the <new>
item will be prepended to <set>
,
if the <index>
is greater than or equal to <set>.size
then <new>
is
appended.
Replace the value at index without sorting
<set>.replaceAt(<index>, <new>, false)
-> <set>
Here, if <index>
is less than 0
or greater than or equal to <set>.size
<new>
will always be appended to <set>
.
Note that when sorting large sets this can get expensive.
<set>.sort(..)
Sort <set>
(in-place)
<set>.sort()
-> <set>
<set>.sort(<cmp>)
-> <set>
In the general case this is similar to
<array>.sort(..)
with the addition of the <array>.sortAs(..)
's ability to sort
as a list
<set>.sort(<sorted-values>)
-> <set>
This is similar to <map>.sort(..)
and Object.sort(..)
,
see the later for more info.
<set>.filter(..)
/ <set>.map(..)
/ <set>.forEach(..)
/ <set>.reduce(..)
/ <set>.reduceRight(..)
For more info see corresponding stoppable methods in
Array
's section.
Date
require('ig-types/Date')
Date.timeStamp(..)
Generate a timestamp (format: 'YYYYMMDDHHMMSS'
)
Date.timeStamp()
-> <timestamp>
Generate a full timestamp, including milliseconds (format: 'YYYYMMDDHHMMSSmmm'
)
Date.timeStamp(true)
-> <timestamp>
This is a shorthand to: (new Date()).getTimeStamp(..)
The timestamp is generated from the time of call, for generating timestamps form specific <date>
objects see:
<date>.getTimeStamp(..)
Date.fromTimeStamp(..)
Create a <date>
from a timestamp
Date.fromTimeStamp(<timestamp>)
-> <date>
This is a shorthand to: (new Date()).setTimeStamp(<timestamp>)
Date.str2ms(..)
Convert a string describing a time period into milliseconds.
Date.str2ms(<str>)
-> <number>
Examples:
// time units (d/h/m/s/ms) and their variants...
var a = Date.str2ms('3 seconds') // -> 3000
var b = Date.str2ms('0.1h') // -> 360000
// time period (DD:HH:MM:SS:mmm)...
var c = Date.str2ms('00:20:001') // -> 20001
var d = Date.str2ms('1:3') // -> 63000
Note that time periods are seconds-based by default unless it contains three digits then it defaults to milliseconds:
// least significant unit is seconds by default...
var e = Date.str2ms(':3') // -> 3000
// when the least significant unit contains 3 digits it is read as ms...
var f = Date.str2ms(':030') // -> 30
Supported formats:
<str> ::=
<milliseconds>
| <seconds>
| <minutes>
| <hours>
| <days>
| <period>
<milliseconds> ::=
<number>
| <number>ms
| <number>m[illi][-]s[ec[ond[s]]]
<seconds> ::=
<number>s
| <number>s[ec[ond[s]]]
<seconds> ::=
<number>m
| <number>m[in[ute[s]]]
<seconds> ::=
<number>h
| <number>h[our[s]]
<seconds> ::=
<number>d
| <number>d[ay[s]]
<period> ::=
[[[DD:]HH:]MM]:SS[:mmm]
| [[[[DD:]HH:]MM]:SS]:mmm
<date>.toShortDate(..)
Generate a short date string from <date>
(format: 'YYYY-MM-DD HH:MM:SS'
)
<date>.toShortDate()
-> <short-date>
Generate a short date string including milliseconds from <date>
(format: 'YYYY-MM-DD HH:MM:SS:mmm'
)
<date>.toShortDate(true)
-> <short-date>
Note that <short-date>
is directly parseable by new Date(..)
var a = (new Date()).toShortDate(true)
// parse the <short-date> and generate a new short date from it...
var b = (new Date(a)).toShortDate(true)
a == b // -> true
<date>.getTimeStamp(..)
Generate a timestamp from <date>
(format 'YYYYMMDDHHMMSS'
)
<date>.getTimeStamp()
-> <timestamp>
Generate a timestamp from <date>
including milliseconds
(format 'YYYYMMDDHHMMSSmmm'
)
<date>.getTimeStamp(true)
-> <timestamp>
<date>.setTimeStamp(..)
Update a <date>
from a timestamp
<date>.setTimeStamp(<timestamp>)
-> <date>
String
require('ig-types/String')
<string>.capitalize()
Capitalize the first character of a string
<string>.capitalize()
-> <string>
<string>.indent(..)
Indent each line in <string>
by <size>
spaces
<string>.indent(<size>)
-> <string>
Indent/prepend each line in <string>
by the <prefix>
string
<string>.indent(<prefix>)
-> <string>
RegExp
require('ig-types/RegExp')
RegExp.quoteRegExp(..)
Quote regexp reserved characters in a string
RegExp.quoteRegExp(<str>)
-> <str>
This is mainly used to quote strings to be matched as-is within a regular expression.
Promise
require('ig-types/Promise')
or
var promise = require('ig-types/Promise')
Interactive promises
Interactive promises can be sent messages and then handle them.
var printer = Promise.interactive(function(resolve, reject, onmessage){
var buf = []
var state = 'pending'
onmessage(function(type, ...args){
type == 'flush' ?
(buf = buf
.filter(function([type, state, ...args]){
console[type](`(${ state }):`, ...args) }))
: type == 'close' ?
(resolve(...args),
state = 'resolved')
: buf.push([type, state, ...args]) }) })
printer
.send('log', 'some message...')
.send('warn', 'some warning...')
.send('flush')
.send('close')
Note that message handling is independent of promise state, so in the above case we can still populate the buffer and flush it even if the promise is resolved
printer
.send('log', 'some other message...')
.send('flush')
If the user wants to handle messages differently (ignore for example) after the
promise is finalized it is their responsibility
(see: <onmessage>(..)
for more info)
Promise.interactive(..)
Create and interactive promise
Promise.interactive(<handler>)
-> <promise-inter>
The <handler>
accepts one additional argument, compared to the Promise(..)
handler, <onmessage>
, used to register message handlers.
<handler>(<resolve>, <reject>, <onmessage>)
<onmessage>(<message-handler>)
Remove <message-handler>
<onmessage>(<message-handler>, false)
Remove all handlers
<onmessage>(false)
<message-handler>
is called when a message is sent via
<promise-inter>.send()
.
<promise-inter>.send(..)
Send a message to an interactive promise
<promise-inter>.send()
<promise-inter>.send(...)
-> <promise-inter>
Sending a message triggers message handlers registered via <onmessage>(..)
passing each handler the arguments.
<promise-inter>.then(..)
Extended .then(..)
implementation.
See <promise-iter>.then(..)
for details.
Cooperative promises
A cooperative promise is one that can be finalized externally/cooperatively.
This can be useful for breaking recursive dependencies between promises or when it is simpler to thread the result receiver promise down the stack than building a promise stack and manually threading the result up.
Example:
// NOTE: implementing this via Promise.any(..) would also require implementing a
// way to stop the "workers" after the result is found...
async function controller(trigger){
while(!trigger.isSet)
// do things...
trigger.isSet
|| trigger.set(result) } }
async function controlled(trigger){
// do things independently of trigger...
trigger
.then(function(){
// do things after trigger...
}) }
var t = Promise.cooperative()
// multiple cooperative controllers competing to create a result...
controller(t)
controller(t)
controller(t)
// ...
// prepare and process result...
// NOTE: calling .then() here is completely optional and done out of role
// hygene -- isolating cooperative API from the client...
controlled(t.then())
// ...
Note that this functionally can be considered a special-case of an interactive promise, but in reality they are two different implementations, the main differences are:
- Cooperative promise constructor does not need a resolver function,
-
Cooperative promises do not the implement
.send(..)
API.
Note that implementing Cooperative promises on top of Interactive promises
cleanly, though feeling more "beautiful", would be more complex than the
current standalone implementation, as it would require both implementing
the .set(..)
API/logic and active encapsulation of the message API.
Promise.cooperative()
Create a cooperative promise
Promise.cooperative()
-> <promise-coop>
<promise-coop>.set(..)
Resolve <promise-coop>
with <value>
<promise-coop>.set(<value>)
<promise-coop>.set(<value>, true)
-> <promise-coop>
If <value>
is a promise, then <promise-coop>
will be bound to its state, i.e.
resolved if <value>
is resolved and rejected if it is rejected with the same
values.
Reject <promise-coop>
with <value>
<promise-coop>.set(<value>, false)
-> <promise-coop>
Calling .set(..)
will set .isSet
to true
.
<promise-coop>.isSet
Property representing if the cooperative promise was set / .set(..)
was
called (value is true
) or no (false
).
This property is read-only.
<promise-coop>.then(..)
Extended .then(..)
implementation.
See <promise-iter>.then(..)
for details.
Promise iteration
An iterable promise is on one hand very similar to Promise.all(..)
in that it
generally takes a list of values each could be either an explicit value or a
promise, and it is similar to a generator in that it allows iteration over the
contained values and chaining of operations but unlike Promise.all(..)
this
iteration occurs depth-first instead of breadth first.
One can think of promise iterators vs. generators as the former being internally controlled and asynchronous while the later being externally controlled and synchronous.
Here is a traditional example using Promise.all(..)
:
var p = Promise.all([ .. ])
// this will not execute until ALL the inputs resolve...
.then(function(lst){
return lst
.filter(function(e){
// ...
})
// this will not run until ALL of lst is filtered...
.map(function(e){
// ...
}) })
And a promise iterator:
var p = Promise.iter([ .. ])
// each element is processed as soon as it is ready disregarding of its order
// in the input array...
.filter(function(e){
// ...
})
// items reach here as soon as they are returned by the filter stage handler...
.map(function(e){
// ...
})
// .then(..) explicitly waits for the whole list of inputs to resolve...
.then(function(lst){
// ...
})
This approach has a number of advantages:
- items are processed as soon as they are available without waiting for the slowest promise on each level to resolve
- simpler and more intuitive code
And some disadvantages:
- item indexes are unknowable until all the promises resolve.
Calling each of the <promise-iter>
methods will return a new and unresolved
promise, even if the original is resolved.
If all values are resolved the <promise-iter>
will resolve on the next
execution frame.
There are two types of iterator methods here, both are transparent but different in how they process values:
-
Parallel methods
These handle elements as soon as they are available even if the parent promise is not yet resolved. - Proxies These methods simply wait for the main promise to resolve and then call the appropriate method on the result.
Promise iterators directly support for-await-of iteration:
for await (var elem of Promise.iter(/* ... */)){
// ...
}
Promise iteration supports three modes of synchronization:
- handle on ready
.iter(
[value, promise, promise, value], handler)
+ . . +
| R |
+ R | +
| +
+ - - - - - - - - - - - - - - - -> resolve
R - input resolved
A handler is started as soon as it's value is ready/resolved, i.e. for non-promise values start immediately.
- handle sequentially when value is ready and previous handler is started
.seqstartiter(
[value, promise, promise, value], handler)
+ . . . |
| R <-+
++- - >R . . |
| | <-+
+ ++ - - - >+ . |
. | | <-+
+ + - - > + |
. . | <-+
+ - - - - - - - > resolve
^ ^
+------+-- returned promise
R - input resolved
A handler is started as soon as all previous handlers are started and the current value is ready/resolved.
- handle sequentially when value is ready and previous handler is resolved
.seqiter(
[value, promise, promise, value], handler)
+ . . . |
| R <-+
++- - >R . . |
| | <-+
+ ++ . . |
. | |
+ - - - >+ . |
. . | <-+
+ - - > + |
. . | <-+
+ - - - - - - - > resolve
^ ^
+------+-- returned promise
R - input resolved
A handler is started as soon as all previous handlers are done, their return values are resolved and the current value is ready/resolved.
Promise.iter(..)
/ promise.IterablePromise(..)
Create an iterable promise
Promise.iter(<array>)
Promise.iter(<promise>)
-> <promise-iter>
<promise>.iter()
Wrap a promise in an promise iterator.
<promise>.iter()
-> <promise-iter>
If <promise>
resolves to a non-array value it will be treated as a single
element, otherwise the array will be iterated over.
<promise-iter>.iter()
Return a shallow copy of the current promise iterator.
<promise-iter>.iter()
-> <promise-iter>
Promise.seqiter(..)
/ promise.IterableSequentialPromise(..)
<promise>.seqiter()
/ <promise-iter>.seqiter()
Promise.seqstartiter(..)
/ promise.IterableSequentialStartPromise(..)
<promise>.seqstartiter()
/ <promise-iter>.seqstartiter()
<promise-iter>.map(..)
/ <promise-iter>.filter(..)
/ <promise-iter>.reduce(..)
Methods similar but not fully equivalent to Array
's
.map(..)
,
.filter(..)
,
and .reduce(..)
<promise-iter>.map(<handler>)
-> <promise-iter>
<handler>(<elem>)
-> <elem>
<promise-iter>.filter(<handler>)
-> <promise-iter>
<handler>(<elem>)
-> <bool>
<promise-iter>.reduce(<handler>, <state>)
-> <promise>
<handler>(<state>, <elem>)
-> <state>
Note that these are different to Array
's equivalents in some details:
-
<handler>
is not called in the order of element occurrence but rather in the order of elements are resolved/ready. -
<handler>
does not get either the element index or the container.
this is because in out-of-order and depth-first execution the index is unknowable and the container is a promise/black-box.
This is especially critical for .reduce(..)
as the iteration in an order
different from the order of elements can affect actual result if this is
not expected.
.reduce(..)
is also a bit different here in that it will return a basic
<promise>
rather than an iterable promise object as we can't know what
will it will reduce to.
Note that since .reduce(..)
handler's execution order can not be known,
there is no point in implementing .reduceRigth(..)
.
<promise-iter>.between(..)
<promise-iter>.between(<value>)
-> <promise-iter>
<promise-iter>.between(<func>)
-> <promise-iter>
<func>([<pre>, <post>], <index-in>, <index-out>, <promise-iter>)
-> <value>
<promise-iter>.flat(..)
<promise-iter>.flat()
<promise-iter>.flat(<depth>)
-> <promise-iter>
This is similar to <array>.flat(..)
see it for more info.
<promise-iter>.reverse()
<promise-iter>.reverse()
-> <promise-iter>
This is deferent from <array>.reverse()
in that it will not reverse in-place,
but rather a reversed copy will be created.
This is similar to <array>.reverse()
see it for more info.
<promise-iter>.concat(..)
<promise-iter>.concat(<other>)
-> <promise-iter>
This is similar to <array>.concat(..)
see it for more info.
<promise-iter>.push(..)
/ <promise-iter>.unshift(..)
<promise-iter>.push(<elem>)
-> <promise-iter>
<promise-iter>.unshift(<elem>)
-> <promise-iter>
These are similar to <array>.push(..)
and <array>.unshift(..)
see them for more info.
<promise-iter>.at(..)
/ <promise-iter>.first()
/ <promise-iter>.last()
Proxies to the appropriate array methods with a special-case: when getting elements
at positions 0
or -1
(i.e. .first()
/ .last()
) these can resolve before the
parent <promise-iter>
.
XXX
<promise-iter>.join(..)
XXX
<promise-iter>.some(..)
/ <promise-iter>.find(..)
<promise-iter>.some(<func>)
-> <promise>
<promise-iter>.find(<func>)
-> <promise>
The main difference between .some(..)
and .find(..)
is in that the <promise>
returned from the former will resolve to either true
or false
, and in the later
to the found value or undefined
.
.find(..)
supports an additional argument that controls what returned <promise>
is resolved to...
<promise-iter>.find(<func>)
<promise-iter>.find(<func>, 'value')
-> <promise>
<promise-iter>.find(<func>, 'bool')
-> <promise>
<promise-iter>.find(<func>, 'result')
-> <promise>
-
value
(default)
resolve to the stored value if found andundefined
otherwise. -
bool
resolve totrue
if the value is found andfalse
otherwise, this is how.some(..)
is impelemnted. -
result
resolve to the return value of the test<func>
.
These are similar to <array>.some(..)
and <array>.find(..)
see them for more info.
<promise-iter>
Array proxy methods returning <promise-iter>.sort(..)
<promise-iter>.slice(..)
-
<promise-iter>.entries()
/<promise-iter>.keys()
/<promise-iter>.values()
These methods are proxies to the appropriate array methods.
<promise-iter>.<method>(..)
-> <promise-iter>
These methods need the parent <promise-iter>
to resolve before resolving themselves.
XXX links...
<promise>
Array proxy methods returning a <promise-iter>.indexOf(..)
<promise-iter>.includes(..)
<promise-iter>.every(..)
<promise-iter>.findIndex(..)
These methods are proxies to the appropriate array methods.
<promise-iter>.<method>(..)
-> <promise>
These methods need the parent <promise-iter>
to resolve before resolving themselves.
Since the equivalent array methods do not return iterables these will return a basic
(non-iterable) <promise>
.
XXX links...
<promise-iter>.then(..)
/ <promise-iter>.catch(..)
/ <promise-iter>.finally(..)
An extension to
<promise>.then(..)
API
this adds the ability to pass no arguments
<promise-iter>.then()
-> <promise>
This will return a generic promise wrapper passing through the results as-is. This can be useful to hide the extended promise API from further code.
promise.IterablePromise.STOP
/ promise.IterablePromise.STOP(..)
A special object that when thrown from a function/promise handler will stop further iteration.
This is undefined
until the ig-types/Array
module is loaded.
For more info see: Stopping the iteration below, and the 'Array' STOP section
<promise-iter>.iterthen(..)
Like .then(..)
but will return an IterablePromise
instance.
<promise-iter>.isSync()
Return true
if all content is resolved, otherwise return false
.
<promise-iter>.sync(..)
If all content is resolved return the promise value, otherwise return a promise.
For more info see: <promise>.sync(..)
Advanced handler
Promise.iter(<block>, <handler>)
-> <iterable-promise>
The <handler>
will get passed each resolved <value>
of the input <block>
as soon as it's available/resolved.
The <handler>
return value is unwrapped into the resulting array, allowing
each call to both remove elements (i.e. returning []
) from the resulting
<block>
as well as insert multiple items (by returning an array of items).
<handler>(<value>)
-> []
-> [ <elem>, .. ]
-> <non-array>
<block> ::=
[]
| [ <elem>, .. ]
<elem> ::=
<value>
| <promise>(<value>)
Example:
var p = Promise.iter(
[1, 2, 3, Promise.resolve(4), [5, 6]],
function(elem){
// duplicate even numbers...
return elem % 2 == 0 ?
[elem, elem]
// return arrays as-is...
: elem instanceof Array ?
[elem]
// remove other elements...
: [] })
.then(function(lst){
console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
Stopping the iteration
Like the Array
module, this support throwing STOP
to
stop iteration. As we uses .smap(..)
stopping support is supported if ig-types/Array
module is loaded.
require('ig-types/Array')
This is also different semantically, as promise iteration can happen out of order,
stopping affects the order of processing and not order of the input array with one exception: promises already created can not be stopped in JavaScript
.
Any handler function passed to a <promise-iter>
method can throw
a STOP.
For more details see: the 'Array' STOP section
Promise proxies
Promise proxies generate a set of prototype methods returning promises that when the parent promise is resolved will resolve to a specific method call.
Example:
var o = {
method: function(...args){
console.log('method:', ...args)
},
}
var p = Peomise.cooperative().as(o)
p.method(1, 2, 3) // returns a promise...
// ...
// resolving a promise will trigger all the proxy emthod execution, so
// here 'method: 1, 2, 3' will get printed...
p.set(o)
<promise>.as(..)
Create a promise proxy
<promise>.as(<object>)
<promise>.as(<constructor>)
-> <promise-proxy>
A proxy promise will be populated with proxy methods to all the methods of the <object>
or <constructor>.prototype
.
<promise-proxy>.<method>(..)
When <promise>
resolves, call the .<method>(..)
on the resolved value.
<promise-proxy>.<method>(..)
-> <method-promise>
<method-promise>
will resolve the the return value of the <method>
when
the main <promise>
is resolved.
Sync/async promise
The goal of this is to handle both sync and asynchronous data flows with one promise-like API, if all of the data can be obtained in a sync manner this will be sync otherwise we will revert to a normal promise.
Promise.sync(..)
/ promise.SyncPromise(..)
Promise.sync(<func>)
-> <sync-promise>
-> <promise>
<func>(<resolve>, <reject>)
<resolve>(<value>)
<reject>(<error>)
Implements the full Promise
protocol but does it in a sync manner, but
the execution of <func>
is done synchronously. If the value passed to
<resolve>(..)
is a promise this will return that and continue asynchronously
otherwise all the promise API (.then(..)
/.catch(..)
/...) is run in sync.
<promise>.sync(..)
Synchronously return the resolved value if <sync-promise>
resolved, and
if it rejected then re-throw the <error>
. Normal promises will return self.
<promise>.sync()
-> <value>
-> <promise>
To suppress errors pass false
to .sync(..)
and to handle them differently
pass an error handler function.
<promise>.sync(false)
-> <value>
<promise>.sync(<onerror>)
-> <value>
<onerror>(<error>)
-> <value>
<sync-promise>.value
/ <sync-promise>.error
<sync-promise>
attributes that provide access the resolved .value
and/or
rejection .error
.
Promise.sync.all(..)
/ Promise.sync.allSettled(..)
/ Promise.sync.any(..)
/ Promise.sync.race(..)
Equivalents to Promise
's respective versions but will run sync if the
relevant items in the input are either non-promises or <sync-promise>
s.
Promise utilities
Promise.awaitOrRun(..)
Await for inputs if any of them is a promise and then run a function with the results, otherwise run the function in sync.
Promise.awaitOrRun(<value>, <func>[, <onerror>])
Promise.awaitOrRun(<value>, .. , <func>[, <onerror>])
-> <promise(value)>
-> <value>
Note that if the last <value>
is a function and no <onerror>
function
is given then .awaitOrRun(..)
will confuse the <value>
for <func>
,
to avoid this one needs to explicitly pass null
/undefined
as <onerror>
.
Special-case: this will expand async generators if they define .then(..)
,
this may change in the future.
Generator extensions and utilities
var generator = require('ig-types/generator')
The basics
The generator hierarchy in JavaScript is a bit complicated.
Consider the following:
// generator constructor function...
var Iter = function*(L){
for(var e of L){
yield e }}
// generator instance...
var iter = Iter([1, 2, 3])
We can test that iter
is an instance of Iter
:
iter instanceof Iter // -> true
Note that there is no generator constructor constructor or meta-generator,
i.e. Iter
is created syntactically and not constructed via a new
constructor.
Due to the three level structure of generators we use a slightly different terminology to reference different levels of API's:
-
Generator
- the generator meta-constructor.
This is a constructor that is used to create/prototype<Generator>
's, i.e. generator constructors.
Generator
is mainly used forinstanceof
checks, but can be used as a prototype for extending generators. -
<Generator>
- the generator constructor.
This is the product of either aGenerator
meta-constructor or afunction*(..){ .. }
statement.
In the above exampleIter
is a generator constructor. -
<generator>
- the generator instance.
Generator instances are created by calling a<Generator>
/ generator constructor.
In the above exampleiter
is a generator instance.
Iterators and generators are similar but not the same. Some objects like Array
's,
Map
's and Set
's provide a number of generic iterators that are not implemented
as generators. These objects are also extended by ig-types/generator
to match the
<generator>
object API defined below.
generator.Generator
Exposes the hidden JavaScript generator meta-constructor.
This is similar to the JavaScript's
Function
constructor
var g = generator.Generator(`
yield 123
yield 321 `)
// prints 123 then 321...
for(var e of g()){
console.log(e.value) }
This can be used to test if a function is a generator constructor
Iter instanceof generator.Generator // -> true
Note that currently in JavaScript there is no built-in way to test if a
constructor/function, Iter
in this case, is a generator constructor.
generator.iter(..)
Generic generator wrapper
generator.iter()
-> <generator>
generator.iter(<iterable>)
-> <generator>
Example:
for(var i of generator.iter([1, 2, 3])){
console.log(i) }
The following are equivalent:
var a = generator.iter()
var b = new generator.Generator()
But Generator()
takes no arguments and thus can not be used as a wrapper
while .iter(..)
is designed to accept an iterable value like an array object.
generator.STOP
Generator instance iteration
This is a set of Array
-like iterator methods that enable chaining of
generators and Promise
-like API to handle the generated results.
Chained generators handle items depth-first, i.e. the items are passed as they are yielded down the generator chain.
<generator>.iter(..)
Iterate over the generator.
<generator>.iter()
-> <generator>
XXX move this to generator.iter(..)
Compatible with <array>
's .iter()
.
.iter(..)
also supports a handler function
<generator>.iter(<handler>)
-> <generator>
<handler>(<elem>, <index>)
-> <elem>
-> [<elem>, ..]
-> []
Note that the iterables returned by <handler>(..)
will be expanded, to prevent
this wrap them in an array.
<generator>.map(..)
/ <generator>.filter(..)
Equivalents to Array
's .map(..)
, .filter(..)
and .reduce(..)
but return
generators that yield the handler return values.
.map(..)
here also supports a generator as a handler
var expand = function*(n){
yield* (new Array(n)).fill(n) }
// will create: [1, 2, 2, 3, 3, 3]
var L = [1,2,3]
.iter()
.map(expand)
.toArray()
Throwing STOP
form within the handler will stop generation, throwing
STOP(<value>)
will yield the <value>
then stop.
var stopAt = function(n){
return function(e){
if(e == n){
// stop iteration yielding the value we are stopping at...
throw generator.STOP(e) }
return e } }
var L = [1,2,3,4,5]
.iter()
.map(stopAt(3))
.toArray()
These also support passing the <onstop>
callback as additional last
argument. It will be called when STOP
/STOP(<value>)
is thrown or
returned.
<generator>.reduce(..)
/ <generator>.greduce(..)
<generator>.forEach(..)
(EXPERIMENTAL)
<generator>.forEach(<func>)
-> <array>
This is different from the above in that this will unwind the <generator>
.
Note that this differs from <array>.forEach(..)
in that his will return the
resulting array, essentially behaving like .map(..)
.
<generator>.between(..)
<generator>.between(<value>)
-> <generator>
<generator>.between(<func>)
-> <generator>
<func>([<pre>, <post>], <index-in>, <index-out>, <generator>)
-> <value>
<generator>.slice(..)
<generator>.slice()
<generator>.slice(<from>)
<generator>.slice(<from>, <to>)
-> <generator>
Note that this does not support negative indexes as it not possible to know the generator length until it is fully done.
Otherwise this is similar to Array
's .slice(..)
but will return a generator
instead of an array, for more info see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
<generator>.at(..)
/ <generator>.gat(..)
<generator>.at(<index>)
-> <value>
-> undefined
<generator>.gat(<index>)
-> <generator>
<generator>.flat(..)
<generator>.flat()
<generator>.flat(<depth>)
-> <generator>
Equivalent to Array
's .flat(..)
but will return a generator instead of an
array, for more info see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
<generator>.shift()
/ <generator>.pop()
/ <generator>.gshift()
/ <generator>.gpop()
Return the first/last item in generator.
<generator>.shift()
<generator>.pop()
-> <value>
-> undefined
Return a <generator>
that will yield the first/last item in the generator.
<generator>.gshift()
<generator>.gpop()
-> <generator>
Note that there are no equivalents to .push(..)
and .unshift(..)
as they
would require breaking item processing order.
Note that .shift()
/.gshift()
will yield the item the generator is at at
time of call, this may not be the first item if the generator is partially
depleted.
<generator>.unshift(..)
/ <generator>.push(..)
Add a value to the generator sequence at start/end.
<generator>.unshift(<value>)
<generator>.push(<value>)
-> <generator>
Value added by .unshift(..)
will be yielded by <generator>
"first", i.e. on
next call to .next()
, regardless of the current generator state.
<generator>.join(..)
XXX
<generator>.unwind(..)
XXX
<generator>.then(..)
/ <generator>.catch(..)
/ <generator>.finally(..)
Return a promise and resolve it with the generator value.
<generator>.then()
-> <promise>
Adding handlers to the promise
<generator>.then(<resolve>, <reject>)
-> <promise>
<generator>.then(<resolve>)
-> <promise>
<generator>.finally(<handler>)
-> <promise>
Note that this will deplete the generator.
These are the same as equivalent Promise
methods, for more info see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
<generator>.toArray()
Unwind a generator into an array
<generator>.toArray()
-> <array>
This is equivalent to [...<generator>]
but more suited for the concatenative style.
Treating iterators the same as generators
Most iterator methods of Array
, Set
and Map
are extended with the same
API supported by the <generator>
, so
effectively most built-in iterator methods can be transparently treated as
generators.
// this will generate: [1, 4, 9]
var L = [ ...[1, 2, 3]
// Note that this is implemented as an iterator in JS and not a generator...
.values()
.map(function(e){
return e * e }) ]
Generator constructor iteration
This API is essentially the same as generator iteration with some minor omissions, but will return a reusable generator pipeline instead of a generator.
var sumOdds = generator.iter
.filter(function(e){
return e % 2 == 1 })
.reduce(function(r, e){
return r + e }, 0)
.pop()
// sumOdds(..) is essentially a function that can be reused...
console.log(sumOdds([1, 2, 3])) // -> 4
console.log(sumOdds([1, 2, 3, 4, 5, 6, 7])) // -> 16
The above code is the same in function to:
var sumOdds = function(lst){
return generator.iter(lst)
.filter(function(e){
return e % 2 == 1 })
.reduce(function(r, e){
return r + e }, 0)
.pop() }
console.log(sumOdds([1, 2, 3])) // -> 4
console.log(sumOdds([1, 2, 3, 4, 5, 6, 7])) // -> 16
<Generator>
methods fall into two categories:
-
<constructor>
create a new chained<Generator>
object that when called will return a<generator>
-
<finalizer>
create a chained function that when called will return a<value>
/undefined
<Generator>.<constructor>(..)
-> <Generator>
<Generator>.<finalizer>(..)
-> <func>
<Generator>(<iterable>)
-> <generator>
<func>(<iterable>)
-> <value>
-> undefined
<Generator>.iter(..)
This is a shorthand to iter(..)
.
This is here mainly for compatibility with
Array
's .iter(..)
.
<Generator>.at(..)
/ <Generator>.gat(..)
<Generator>.at(<index>)
-> <func>
<Generator>.gat(<index>)
-> <Generator>
Equivalents to <generator>
's .at(..)
/.gat(..)
but returning a reusable <func>
/<Generator>
.
<Generator>.shift()
/ <Generator>.pop()
/ <Generator>.gshift()
/ <Generator>.gpop()
<Generator>.shift()
<Generator>.pop()
-> <func>
<Generator>.gshift()
<Generator>.gpop()
-> <Generator>
Note that .shift()
/.gshift()
will get the element the generator is at
currently which may not be the first element in the sequence.
Equivalents to <generator>
's .shift(..)
/.pop(..)
/..
but returning a reusable <func>
/<Generator>
.
<generator>.unshift(..)
/ <generator>.push(..)
<Generator>.unshift(<value>)
<Generator>.push(<value>)
-> <Generator>
Equivalents to <generator>
's .unshift(..)
/.push(..)
but returning a reusable <Generator>
.
<Generator>.slice(..)
<Generator>.slice(<from>)
<Generator>.slice(<from>, <to>)
-> <Generator>
Unlike Array
's .slice(..)
this does not support negative indexes.
Equivalent to <generator>
's .slice(..)
but returning a reusable <Generator>
.
<Generator>.map(..)
/ <Generator>.filter(..)
/ <Generator>.reduce(..)
/ <Generator>.flat()
Counterparts to <generator>
's
.map(..)
, .filter(..)
, .reduce(..)
/.greduce(..)
and
.flat(..)
but return a <Generator>
.
<Generator>.between(..)
<Generator>.between(<value>)
-> <Generator>
<Generator>.between(<func>)
-> <Generator>
<func>([<pre>, <post>], <index-in>, <index-out>, <Generator>)
-> <value>
<Generator>.toArray()
Return a function that will return a <generator>
output as an Array
.
<Generator>.toArray()
-> <function>
<Generator>.join(..)
XXX
<Generator>.unwind(..)
Generator combinators
<Generator>.chain(..)
/ <generator>.chain(..)
<Generator>.chain(<Generator>, ..)
-> <Generator>
<generator>.chain(<Generator>, ..)
-> <generator>
// double each element...
var x2 = generator.iter
.map(function(e){ return e * 2 })
generator.range(0, 100).chain(x2)
<Generator>.concat(..)
/ <generator>.concat(..)
Concatenate the results from generators
<Generator>.concat(<Generator>, ..)
-> <Generator>
<generator>.concat(<generator>, ..)
-> <generator>
Generator library
generator.range(..)
Create a generator yielding a range of numbers
range()
range(<to>)
range(<from>, <to>)
range(<from>, <to>, <step>)
-> <generator>
generator.repeat(..)
Create a generator repeatedly yielding <value>
repeat()
repeat(<value>)
repeat(<value>, <stop>)
-> <generator>
<stop>(<value>)
-> <bool>
If no value is given true
is yielded by default.
<stop>
if given will be called with each <value>
before it is yielded and
if it returns false
the iteration is stopped.
generator.produce(..)
Create a generator calling a function to produce yielded values
produce()
produce(<func>)
-> <generator>
<func>()
-> <value>
<func>(..)
can throw
STOP
or STOP(<value>)
to stop production at any time.
Generator helpers
generator.stoppable(..)
Wrap function/generator adding support for stopping mid-iteration by throwing STOP
.
stoppable(<generator>)
-> <generator>
Async generator extensions
XXX EXPERIMENTAL
generator.AsyncGenerator
<async-generator>.unwind(..)
<async-generator>.then(..)
/ <async-generator>.catch(..)
/ <async-generator>.finally(..)
<async-generator>.iter(..)
<async-generator>.map(..)
/ <async-generator>.filter(..)
/ <async-generator>.reduce(..)
<async-generator>.chain(..)
<async-generator>.flat(..)
<async-generator>.concat(..)
<async-generator>.push(..)
/ <async-generator>.unshift(..)
Containers
var containers = require('ig-types').containers
or, to only import containers:
var containers = require('ig-types/containers')
Note that this will also import ig-types/Map
.
containers.UniqueKeyMap()
(Map
)
UniqueKeyMap
implements a key-value container (i.e. Map
) that supports
and maintains duplicate keys by appending an index to them.
The original keys are stored internally thus the renaming mechanics are
stable.
UniqueKeyMap
extends the Map
constructor, so all the usual Map
methods and properties apply here.
To construct an instance:
var x = new UniqueKeyMap()
or:
// new is optional...
var y = UniqueKeyMap()
UniqueKeyMap
supports the same initialization signature as Map
but
treats repeating keys differently.
var z = UniqueKeyMap([['a', 1], ['a', 2], ['b', 1]])
The second "a"
item will automatically get re-keyed as "a (1)"
:
console.log([...z.keys()]) // -> ['a', 'a (1)', 'b']
Note that .set(..)
will never rewrite an element:
z.set('a', 3)
console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (2)']
z.get('a') // -> 1
z.get('a (1)') // -> 2
To get the generated key:
var k = z.set('a', 4, true)
console.log(k) // -> 'a (3)'
To explicitly rewrite an item:
z.reset('a (1)', 4)
z.get('a (1)') // -> 4
And we can rename items, i.e. change their key:
z.rename('a (2)', 'c')
console.log([...z.keys()]) // -> ['a', 'a (1)', 'b', 'a (3)', 'c']
For more info on Map
see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
<unique-key-map>.set(..)
<unique-key-map>.reset(<key>, <item>)
-> <unique-key-map>
<unique-key-map>.reset(<key>, <item>, true)
-> <new-key>
Add an <item>
to <unique-key-map>
.
If <key>
already exists then add an index to it to make it unique.
Key updating is done via <unique-key-map>.__key_pattern__
.
<unique-key-map>.reset(..)
<unique-key-map>.reset(<key>, <item>)
-> <unique-key-map>
Explicitly write an <item>
under <key>
as-is, this is like Map
's .set(..)
.
<unique-key-map>.rename(..)
<unique-key-map>.rename(<from-key>, <to-key>)
-> <unique-key-map>
<unique-key-map>.rename(<from-key>, <to-key>, true)
-> <new-key>
Rename item key from <from-key>
to <to-key>
.
Same mechanics apply as for .set(..)
for key uniqueness.
Note, if .__unordered_rename__
is
false
(default) this calls .orderedRename(..)
otherwise .unorderedRename(..)
is called.
<unique-key-map>.orderedRename(..)
<unique-key-map>.unorderedRename(..)
<unique-key-map>.keysOf(..)
<unique-key-map>.originalKey(..)
<unique-key-map>.uniqueKey(..)
<unique-key-map>.__key_pattern__
<unique-key-map>.__unordered_rename__
Event
This module defines a set of pure-JavaScript event-like method constructors and utilities.
var event = require('ig-types/event')
event.Eventfull(..)
Create and eventful method.
event.Eventfull(<name>[, <options>])
event.Eventfull(<name>, <func>[, <options>])
-> <method>
An eventful method is a method that can be called either directly or via
.trigger(<method-name>, ..)
.
Calling an eventful method will call <func>(..)
if defined and will
trigger the eventful method's handlers trigger, either after <func>(..)
returns or when <hander>(..)
is called within <func>(..)
.
<method>(..)
-> <value>
Note that if <func>(..)
returns undefined
then <method>(..)
will
return either this
or undefined
depending on <options>.defaultReturn
being set to 'context'
(default) or undefined
resp.
Handlers can be bound to an eventful method via .on(<method-name>, ...)
,
unbound via .off(<method-name>)
,
see: event.EventHandlerMixin
for more info.
<handler>(<event-name>, ...)
The event <func>(..)
gets the <handle-func>(..)
as first argument
followed by the arguments passed to <method>(..)
when called.
<func>(<handle-func>, ...)
-> <value>
<handle-func>(..)
controls when the event handlers are called.
<handle-func>()
<handle-func>(true)
-> true
-> false
<handle-func>(true, ...)
-> true
-> false
Calling <handle-func>(..)
is optional, if not called the handlers will
get triggered after <func>(..)
returns.
Passing false
to <handle-func>(..)
will prevent the event handlers
from being called.
<handle-func>(false)
-> undefined
Note that for async
event <func>(..)
it might be useful to trigger
the handlers after the promise resolves, this can be done by first calling
<handle-func>(false)
to prevent the handlers from being triggered as soon
as the promise is returned and after an appropriate await
ing calling
<handle-func>()
to actually trigger the handlers.
Example:
var evt = event.Event('evt', async function(handler, ...args){
handler(false)
var value = await something()
handler()
return value })
Special case: <event-commands>
<event-command>
s are instances of EventCommand
that are handled by
event methods in a special way.
<method>(<event-command>, ...)
-> <value>
<func>(<handle-func>, <event-command>, ...)
-> <value>
EventCommand
instance can be passed as the first argument of <method>(..)
,
in this case the event function will get it but the event handlers
will not.
This is done to be able to externally pass commands to event methods that get handled in a special way by the function but not passed to the event handlers.
For an example of a built-in <event-command>
see: event.TRIGGER
event.Event(..)
Extends Eventful(..)
adding ability to bind events via the resulting
method directly by passing it a function.
<method>(<handler>)
-> <this>
event.PureEvent(..)
Like Event(..)
but produces an event method that can only be triggered
via .trigger(name, ...), calling this is a no-op.
event.TRIGGER
Special value (<event-command>
) that when passed to an event method as
first argument will force it to trigger event if the first argument was
a function.
event.EventHandlerMixin
A mixin defining the basic event API.
For more info on mixins see: https://github.com/flynx/object.js#mixin
<obj>.on(..)
Bind a handler to an event or an eventful method.
<obj>.on(<event-name>, <handler>)
-> <obj>
<obj>.one(..)
Like <obj>.on(..)
but the <handler>
will be called only once.
<obj>.one(<event-name>, <handler>)
-> <obj>
<obj>.off(..)
Unbind <handler>
from event.
<obj>.off(<event-name>, <handler>)
-> <obj>
<obj>.trigger(..)
Trigger an event.
<obj>.trigger(<event-name>)
<obj>.trigger(<event-name>, ...)
-> <obj>
event.EventDocMixin
A mixin defining the basic event introspection API.
For more info on mixins see: https://github.com/flynx/object.js#mixin
<obj>.eventfull
Property listing the eventful method names in the current context.
<obj>.events
Property listing the event method names in the current context, this also will include eventful method names.
event.EventMixin
Combines event.EventHandlerMixin
and
event.EventDocMixin
.
For more info on mixins see: https://github.com/flynx/object.js#mixin
Runner
var runner = require('ig-types/runner')
Micro task queue
This includes event.EventMixin
.
runner.STOP
runner.SKIP
Queue(..)
/ Queue.runTasks(..)
Queue.handle(..)
Create a handler queue object.
Queue.handle(<func>, ...<data>)
Queue.handle(<options>, <func>, ...<data>)
-> <queue>
A handler queue is a queue that has a single handler function (.handle(..)
)
that handles the queue data.
This is a shorthand for:
var handler_queue = Queue({
handler: function(item){ .. },
..
},
.. )
<queue>.state
<queue>.start(..)
<queue>.stop(..)
<queue>.runTask(..)
<queue>.tasksAdded(..)
(event)
<queue>.taskStarting(..)
(event)
<queue>.taskFailed(..)
(event)
<queue>.taskCompleted(..)
(event)
Event, triggered when a task is completed passing in its result.
<queue>.queueEmpty(..)
(event)
<queue>.prioritize(..)
<queue>.delay(..)
<queue>.add(..)
<queue>.clear(..)
FinalizableQueue(..)
/ FinalizableQueue.runTasks(..)
(Queue)
This is similar to Queue(..)
but adds two terminal states ("done"
and
"aborted"
) and a promise
-mapping.
FinalizableQueue.handle(<func>, ...<data>)
FinalizableQueue.handle(<options>, <func>, ...<data>)
-> <finalizable-queue>
When a <finalizable-queue>
reaches a terminal state it is frozen.
<finalizable-queue>.done(..)
(event/method)
<finalizable-queue>.abort(..)
(event/method)
<finalizable-queue>.promise(..)
<finalizable-queue>.then(..)
<finalizable-queue>.catch(..)
Large task management
runner.TaskManager(..)
This includes event.EventMixin
.
<task-manager>.Task(..)
<task-manager>.sync_start
<task-manager>.record_times
<task-manager>.titled(..)
<task-manager>.send(..)
<task-manager>.stop(..)
<task-manager>.done(..)
(event)
<task-manager>.error(..)
(event)
<task-manager>.tasksDone(..)
(event)
runner.TaskTicket(..)
runner.TaskMixin(..)
License
Copyright (c) 2020, Alex A. Naanou,
All rights reserved.