ig-types 6.14.1 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/Array.js +19 -13
  2. package/Promise.js +147 -19
  3. package/README.md +73 -1
  4. package/package.json +1 -1
package/Array.js CHANGED
@@ -139,7 +139,8 @@ var makeChunkIter = function(iter, wrapper){
139
139
  try {
140
140
  // handle iteration...
141
141
  res.push(
142
- val = (chunk = chunks.shift())[iter](_wrapper, ...rest))
142
+ val = (chunk = chunks.shift())
143
+ [iter](_wrapper, ...rest))
143
144
  // handle chunk...
144
145
  postChunk
145
146
  && postChunk.call(that,
@@ -319,9 +320,12 @@ object.Mixin('ArrayProtoMixin', 'soft', {
319
320
  // NOTE: order is preserved...
320
321
  unique: function(normalize){
321
322
  return normalize ?
322
- [...new Map(this.map(function(e){ return [normalize(e), e] })).values()]
323
- // NOTE: we are calling .compact() here to avoid creating undefined
324
- // items from empty slots in sparse arrays...
323
+ [...new Map(this
324
+ .map(function(e){
325
+ return [normalize(e), e] }))
326
+ .values()]
327
+ // NOTE: we are calling .compact() here to avoid creating
328
+ // undefined items from empty slots in sparse arrays...
325
329
  : [...new Set(this.compact())] },
326
330
  tailUnique: function(normalize){
327
331
  return this
@@ -336,8 +340,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
336
340
  // self to other (internal) while match compares two entities
337
341
  // externally.
338
342
  // XXX not sure if we need the destinction in name, will have to
339
- // come back to this when refactoring diff.js -- all three have
340
- // to be similar...
343
+ // come back to this when refactoring diff.js -- all three
344
+ // have to be similar...
341
345
  cmp: function(other){
342
346
  if(this === other){
343
347
  return true }
@@ -353,7 +357,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
353
357
  // NOTE: this will ignore order and repeating elments...
354
358
  setCmp: function(other){
355
359
  return this === other
356
- || (new Set([...this, ...other])).length == (new Set(this)).length },
360
+ || (new Set([...this, ...other])).length
361
+ == (new Set(this)).length },
357
362
 
358
363
  // Sort as the other array...
359
364
  //
@@ -369,12 +374,13 @@ object.Mixin('ArrayProtoMixin', 'soft', {
369
374
  // This will sort the intersecting items in the head keeping the rest
370
375
  // of the items in the same relative order...
371
376
  //
372
- // NOTE: if an item is in the array multiple times only the first index
373
- // is used...
377
+ // NOTE: if an item is in the array multiple times only the first
378
+ // index is used...
374
379
  //
375
380
  // XXX should this extend/patch .sort(..)???
376
- // ...currently do not see a clean way to do this without extending
377
- // and replacing Array or directly re-wrapping .sort(..)...
381
+ // ...currently do not see a clean way to do this without
382
+ // extending and replacing Array or directly re-wrapping
383
+ // .sort(..)...
378
384
  sortAs: function(other, place='head'){
379
385
  place = place == 'tail' ? -1 : 1
380
386
  // NOTE: the memory overhead here is better than the time overhead
@@ -441,8 +447,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
441
447
 
442
448
  // Convert an array to a map...
443
449
  //
444
- // This is similar to Array.prototype.toKeys(..) but does not restrict
445
- // value type to string.
450
+ // This is similar to Array.prototype.toKeys(..) but does not
451
+ // restrict value type to string.
446
452
  //
447
453
  // Format:
448
454
  // Map([
package/Promise.js CHANGED
@@ -39,7 +39,7 @@ var object = require('ig-object')
39
39
  // Like Promise.all(..) but adds ability to iterate through results
40
40
  // via generators .map(..)/.reduce(..) and friends...
41
41
  //
42
- // NOTE: it would be nice to support throwing STOP (XXX) from the iterable
42
+ // NOTE: it would be nice to support throwing STOP from the iterable
43
43
  // promise but...
44
44
  // - this is more complicated than simply using .smap(..) instead
45
45
  // of .map(..) because the list can contain async promises...
@@ -48,7 +48,7 @@ var object = require('ig-object')
48
48
  // ...then there's a question of derivative iterators etc.
49
49
  // - another issue here is that the stop would happen in order of
50
50
  // execution and not order of elements...
51
- // XXX though stopping within a single level might be useful...
51
+ // XXX EXPEREMENTAL: STOP...
52
52
  // NOTE: the following can not be implemented here:
53
53
  // .splice(..) - can't both modify and return
54
54
  // a result...
@@ -87,6 +87,11 @@ function(name){
87
87
  var IterablePromise =
88
88
  module.IterablePromise =
89
89
  object.Constructor('IterablePromise', Promise, {
90
+ // XXX EXPEREMENTAL: STOP...
91
+ get STOP(){
92
+ return Array.STOP },
93
+
94
+ }, {
90
95
  // packed array...
91
96
  //
92
97
  // Holds promise state.
@@ -100,14 +105,14 @@ object.Constructor('IterablePromise', Promise, {
100
105
  // ]
101
106
  //
102
107
  // This format has several useful features:
103
- // - concatenating packed list results in a packed list
108
+ // - concatenating packed lists results in a packed list
104
109
  // - adding an iterable promise (as-is) into a packed list results
105
110
  // in a packed list
106
111
  //
107
112
  // NOTE: in general iterable promises are implicitly immutable, so
108
- // it is not recomended to ever edit this inplace...
109
- // NOTE: we are not isolation any internals to enable users to
110
- // responsibly extend the code.
113
+ // it is not recomended to ever edit this in-place...
114
+ // NOTE: we are not isolating or "protecting" any internals to
115
+ // enable users to responsibly extend the code.
111
116
  __packed: null,
112
117
 
113
118
  // low-level .__packed handlers/helpers...
@@ -115,6 +120,15 @@ object.Constructor('IterablePromise', Promise, {
115
120
  // NOTE: these can be useful for debugging and extending...
116
121
  //
117
122
  // pack and oprionally transform/handle an array (sync)...
123
+ //
124
+ // NOTE: if 'types/Array' is imported this will support throwing STOP
125
+ // from the handler.
126
+ // Due to the async nature of promises though the way stops are
127
+ // handled may be unpredictable -- the handlers can be run out
128
+ // of order, as the nested promises resolve and thus throwing
129
+ // stop will stop the handlers not yet run and not the next
130
+ // handlers in sequence.
131
+ // XXX EXPEREMENTAL: STOP...
118
132
  __pack: function(list, handler=undefined){
119
133
  var that = this
120
134
  // handle iterable promise list...
@@ -131,6 +145,57 @@ object.Constructor('IterablePromise', Promise, {
131
145
  handler = handler
132
146
  ?? function(elem){
133
147
  return [elem] }
148
+
149
+ //* XXX EXPEREMENTAL: STOP...
150
+ var stoppable = false
151
+ var stop = false
152
+ var map = 'map'
153
+ var pack = function(){
154
+ return [list].flat()
155
+ [map](function(elem){
156
+ return elem && elem.then ?
157
+ (stoppable ?
158
+ // stoppable -- need to handle stop async...
159
+ elem
160
+ .then(function(res){
161
+ return !stop ?
162
+ handler(res)
163
+ : [] })
164
+ // NOTE: we are using .catch(..) here
165
+ // instead of directly passing the
166
+ // error handler to be able to catch
167
+ // the STOP from the handler...
168
+ .catch(handleSTOP)
169
+ // non-stoppable...
170
+ : elem.then(handler))
171
+ : elem instanceof Array ?
172
+ handler(elem)
173
+ // NOTE: we keep things that do not need protecting
174
+ // from .flat() as-is...
175
+ : !handle ?
176
+ elem
177
+ : handler(elem) }) }
178
+
179
+ // pack (stoppable)...
180
+ if(!!this.constructor.STOP){
181
+ stoppable = true
182
+ map = 'smap'
183
+ var handleSTOP = function(err){
184
+ stop = err
185
+ if(err === that.constructor.STOP
186
+ || err instanceof that.constructor.STOP){
187
+ return 'value' in err ?
188
+ err.value
189
+ : [] }
190
+ throw err }
191
+ try{
192
+ return pack()
193
+ }catch(err){
194
+ return handleSTOP(err) } }
195
+
196
+ // pack (non-stoppable)...
197
+ return pack() },
198
+ /*/
134
199
  return [list].flat()
135
200
  .map(function(elem){
136
201
  return elem && elem.then ?
@@ -143,6 +208,7 @@ object.Constructor('IterablePromise', Promise, {
143
208
  : !handle ?
144
209
  elem
145
210
  : handler(elem) }) },
211
+ //*/
146
212
  // transform/handle packed array (sync)...
147
213
  __handle: function(list, handler=undefined){
148
214
  var that = this
@@ -159,7 +225,14 @@ object.Constructor('IterablePromise', Promise, {
159
225
  // NOTE: since each section of the packed .__array is the same
160
226
  // structure as the input we'll use .__pack(..) to handle
161
227
  // them, this also keeps all the handling code in one place.
228
+ //* XXX EXPEREMENTAL: STOP...
229
+ var map = !!this.constructor.STOP ?
230
+ 'smap'
231
+ : 'map'
232
+ return list[map](function(elem){
233
+ /*/
162
234
  return list.map(function(elem){
235
+ //*/
163
236
  return elem instanceof Array ?
164
237
  that.__pack(elem, handler)
165
238
  : elem instanceof Promise ?
@@ -280,12 +353,6 @@ object.Constructor('IterablePromise', Promise, {
280
353
  return this.constructor([elem])
281
354
  .concat(this) },
282
355
 
283
-
284
- // XXX thses need to stop once an element is found so we cant simply
285
- // use .map(..) or .reduce(..)...
286
- // XXX .find(func) / .findIndex(func)
287
-
288
-
289
356
  // proxy methods...
290
357
  //
291
358
  // These require the whole promise to resolve to trigger.
@@ -316,29 +383,86 @@ object.Constructor('IterablePromise', Promise, {
316
383
 
317
384
  // NOTE: unlike .reduce(..) this needs the parent fully resolved
318
385
  // to be able to iterate from the end.
386
+ // XXX is it faster to do .reverse().reduce(..) ???
319
387
  // XXX ???
320
388
  reduceRight: promiseProxy('reduceRight'),
321
389
 
322
390
  // NOTE: there is no way we can do a sync generator returning
323
- // promises for values because any promise in .__packed makes the
324
- // value count/index non-deterministic...
391
+ // promises for values because any promise in .__packed makes
392
+ // the value count/index non-deterministic...
325
393
  sort: iterPromiseProxy('sort'),
326
- // XXX we could have a special-case here for .slice()/slice(0, -1)
327
- // and possibly othets, should we???
328
394
  slice: iterPromiseProxy('slice'),
329
395
 
330
396
  entries: iterPromiseProxy('entries'),
331
397
  keys: iterPromiseProxy('keys'),
332
- // XXX we could possibly make this better via .map(..)
333
398
  values: iterPromiseProxy('values'),
334
399
 
335
400
  indexOf: promiseProxy('indexOf'),
336
401
  lastIndexOf: promiseProxy('lastIndexOf'),
337
402
  includes: promiseProxy('includes'),
338
403
 
404
+ //
405
+ // .find(<func>)
406
+ // .find(<func>, 'value')
407
+ // -> <promise>(<value>)
408
+ //
409
+ // .find(<func>, 'result')
410
+ // -> <promise>(<result>)
411
+ //
412
+ // .find(<func>, 'bool')
413
+ // -> <promise>(<bool>)
414
+ //
415
+ // NOTE: this is slightly different to Array's .find(..) in that it
416
+ // accepts the result value enabling returning both the value
417
+ // itself ('value', default), the test function's result
418
+ // ('result') or true/false ('bool') -- this is added to be
419
+ // able to distinguish between the undefined as a stored value
420
+ // and undefined as a "nothing found" result.
421
+ // NOTE: I do not get how essentially identical methods .some(..)
422
+ // and .find(..) got added to JS's Array...
423
+ // the only benefit is that .some(..) handles undefined values
424
+ // stored in the array better...
425
+ // NOTE: this will return the result as soon as it's available but
426
+ // it will not stop the created but unresolved at the time
427
+ // promises from executing, this is both good and bad:
428
+ // + it will not break other clients waiting for promises
429
+ // to resolve...
430
+ // - if no clients are available this can lead to wasted
431
+ // CPU time...
432
+ find: async function(func, result='value'){
433
+ var that = this
434
+ // NOTE: not using pure await here as this is simpler to actually
435
+ // control the moment the resulting promise resolves without
436
+ // the need for juggling state...
437
+ return new Promise(function(resolve, reject){
438
+ var resolved = false
439
+ that.map(function(elem){
440
+ var res = func(elem)
441
+ if(res){
442
+ resolved = true
443
+ resolve(
444
+ result == 'bool' ?
445
+ true
446
+ : result == 'result' ?
447
+ res
448
+ : elem)
449
+ // XXX EXPEREMENTAL: STOP...
450
+ // NOTE: we do not need to throw STOP here
451
+ // but it can prevent some overhead...
452
+ if(that.constructor.STOP){
453
+ throw that.constructor.STOP } } })
454
+ .then(function(){
455
+ resolved
456
+ || resolve(
457
+ result == 'bool' ?
458
+ false
459
+ : undefined) }) }) },
460
+ findIndex: promiseProxy('findIndex'),
461
+
462
+ // NOTE: this is just a special-case of .find(..)
463
+ some: async function(func){
464
+ return this.find(func, 'bool') },
339
465
  every: promiseProxy('every'),
340
- // XXX this can be lazy... (???)
341
- some: promiseProxy('some'),
342
466
 
343
467
 
344
468
  // promise api...
@@ -406,6 +530,10 @@ object.Constructor('IterablePromise', Promise, {
406
530
  // Promise.iter(false)
407
531
  // -> iterable-promise
408
532
  //
533
+ //
534
+ // NOTE: if 'types/Array' is imported this will support throwing STOP,
535
+ // for more info see notes for .__pack(..)
536
+ // XXX EXPEREMENTAL: STOP...
409
537
  __new__: function(_, list, handler){
410
538
  // instance...
411
539
  var promise
package/README.md CHANGED
@@ -78,10 +78,13 @@ Library of JavaScript type extensions, types and utilities.
78
78
  - [`<promise-iter>.concat(..)`](#promise-iterconcat)
79
79
  - [`<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`](#promise-iterpush--promise-iterunshift)
80
80
  - [`<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`](#promise-iterat--promise-iterfirst--promise-iterlast)
81
+ - [`<promise-iter>.some(..)` / `<promise-iter>.find(..)`](#promise-itersome--promise-iterfind)
81
82
  - [Array proxy methods returning `<promise-iter>`](#array-proxy-methods-returning-promise-iter)
82
83
  - [Array proxy methods returning a `<promise>`](#array-proxy-methods-returning-a-promise)
83
84
  - [`<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`](#promise-iterthen--promise-itercatch--promise-iterfinally)
85
+ - [`promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`](#promiseiterablepromisestop--promiseiterablepromisestop)
84
86
  - [Advanced handler](#advanced-handler)
87
+ - [Stopping the iteration](#stopping-the-iteration)
85
88
  - [Promise proxies](#promise-proxies)
86
89
  - [`<promise>.as(..)`](#promiseas)
87
90
  - [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
@@ -1674,6 +1677,46 @@ parent `<promise-iter>`.
1674
1677
 
1675
1678
  XXX
1676
1679
 
1680
+ #### `<promise-iter>.some(..)` / `<promise-iter>.find(..)`
1681
+
1682
+ ```bnf
1683
+ <promise-iter>.some(<func>)
1684
+ -> <promise>
1685
+
1686
+ <promise-iter>.find(<func>)
1687
+ -> <promise>
1688
+ ```
1689
+
1690
+ The main difference between `.some(..)` and `.find(..)` is in that the `<promise>`
1691
+ returned from the former will resolve to either `true` or `false`, and in the later
1692
+ to the found value or `undefined`.
1693
+
1694
+ `.find(..)` supports an additional argument that controls what returned `<promise>`
1695
+ is resolved to...
1696
+
1697
+ ```bnf
1698
+ <promise-iter>.find(<func>)
1699
+ <promise-iter>.find(<func>, 'value')
1700
+ -> <promise>
1701
+
1702
+ <promise-iter>.find(<func>, 'bool')
1703
+ -> <promise>
1704
+
1705
+ <promise-iter>.find(<func>, 'result')
1706
+ -> <promise>
1707
+ ```
1708
+
1709
+ - `value` (default)
1710
+ resolve to the stored value if found and `undefined` otherwise.
1711
+ - `bool`
1712
+ resolve to `true` if the value is found and `false` otherwise, this is how
1713
+ `.some(..)` is impelemnted.
1714
+ - `result`
1715
+ resolve to the return value of the test `<func>`.
1716
+
1717
+ These are similar to [`<array>.some(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
1718
+ and [`<array>.find(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
1719
+ see them for more info.
1677
1720
 
1678
1721
  #### Array proxy methods returning `<promise-iter>`
1679
1722
 
@@ -1697,7 +1740,8 @@ XXX links...
1697
1740
 
1698
1741
  - `<promise-iter>.indexOf(..)`
1699
1742
  - `<promise-iter>.includes(..)`
1700
- - `<promise-iter>.every(..)` / `<promise-iter>.some(..)`
1743
+ - `<promise-iter>.every(..)`
1744
+ - `<promise-iter>.findIndex(..)`
1701
1745
 
1702
1746
  These methods are proxies to the appropriate array methods.
1703
1747
 
@@ -1727,6 +1771,16 @@ this adds the ability to pass no arguments
1727
1771
  This will return a generic promise wrapper passing through the results as-is. This
1728
1772
  can be useful to hide the extended promise API from further code.
1729
1773
 
1774
+ #### `promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`
1775
+
1776
+ A special object that when thrown from a function/promise handler will stop
1777
+ further iteration.
1778
+
1779
+ This is `undefined` until the `ig-types/Array` module is loaded.
1780
+
1781
+ For more info see: [Stopping the iteration](#stopping-the-iteration) below, and
1782
+ [the 'Array' STOP section](#arraystop--arraystop)
1783
+
1730
1784
 
1731
1785
  #### Advanced handler
1732
1786
 
@@ -1778,6 +1832,24 @@ var p = Promise.iter(
1778
1832
  console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
1779
1833
  ```
1780
1834
 
1835
+ #### Stopping the iteration
1836
+
1837
+ Like the [`Array`](#arraystop--arraystop) module, this support throwing `STOP` to
1838
+ stop iteration. As we uses [`.smap(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach)
1839
+ stopping support is supported if `ig-types/Array` module is loaded.
1840
+
1841
+ ```javascript
1842
+ require('ig-types/Array')
1843
+ ```
1844
+
1845
+ This is also different semantically, as promise iteration can happen out of order,
1846
+ 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`.
1847
+
1848
+ Any handler function passed to a `<promise-iter>` method can `throw` a STOP.
1849
+
1850
+ For more details see: [the 'Array' STOP section](#arraystop--arraystop)
1851
+
1852
+
1781
1853
 
1782
1854
  ### Promise proxies
1783
1855
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.14.1",
3
+ "version": "6.15.0",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {