ig-types 6.14.1 → 6.15.6

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 (5) hide show
  1. package/Array.js +39 -15
  2. package/Promise.js +170 -41
  3. package/README.md +130 -14
  4. package/generator.js +240 -51
  5. package/package.json +1 -1
package/Array.js CHANGED
@@ -24,6 +24,9 @@ var STOP =
24
24
  module.STOP =
25
25
  object.STOP
26
26
 
27
+ var stoppable =
28
+ module.stoppable =
29
+ generator.stoppable
27
30
 
28
31
 
29
32
  //---------------------------------------------------------------------
@@ -139,7 +142,8 @@ var makeChunkIter = function(iter, wrapper){
139
142
  try {
140
143
  // handle iteration...
141
144
  res.push(
142
- val = (chunk = chunks.shift())[iter](_wrapper, ...rest))
145
+ val = (chunk = chunks.shift())
146
+ [iter](_wrapper, ...rest))
143
147
  // handle chunk...
144
148
  postChunk
145
149
  && postChunk.call(that,
@@ -211,6 +215,7 @@ object.Mixin('ArrayMixin', 'soft', {
211
215
  // done...
212
216
  : [] },
213
217
 
218
+ // XXX add handler function support -- a-la generator.js'
214
219
  iter: function*(lst=[]){
215
220
  yield* lst.iter() },
216
221
  })
@@ -319,9 +324,12 @@ object.Mixin('ArrayProtoMixin', 'soft', {
319
324
  // NOTE: order is preserved...
320
325
  unique: function(normalize){
321
326
  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...
327
+ [...new Map(this
328
+ .map(function(e){
329
+ return [normalize(e), e] }))
330
+ .values()]
331
+ // NOTE: we are calling .compact() here to avoid creating
332
+ // undefined items from empty slots in sparse arrays...
325
333
  : [...new Set(this.compact())] },
326
334
  tailUnique: function(normalize){
327
335
  return this
@@ -336,8 +344,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
336
344
  // self to other (internal) while match compares two entities
337
345
  // externally.
338
346
  // 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...
347
+ // come back to this when refactoring diff.js -- all three
348
+ // have to be similar...
341
349
  cmp: function(other){
342
350
  if(this === other){
343
351
  return true }
@@ -353,7 +361,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
353
361
  // NOTE: this will ignore order and repeating elments...
354
362
  setCmp: function(other){
355
363
  return this === other
356
- || (new Set([...this, ...other])).length == (new Set(this)).length },
364
+ || (new Set([...this, ...other])).length
365
+ == (new Set(this)).length },
357
366
 
358
367
  // Sort as the other array...
359
368
  //
@@ -369,12 +378,13 @@ object.Mixin('ArrayProtoMixin', 'soft', {
369
378
  // This will sort the intersecting items in the head keeping the rest
370
379
  // of the items in the same relative order...
371
380
  //
372
- // NOTE: if an item is in the array multiple times only the first index
373
- // is used...
381
+ // NOTE: if an item is in the array multiple times only the first
382
+ // index is used...
374
383
  //
375
384
  // 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(..)...
385
+ // ...currently do not see a clean way to do this without
386
+ // extending and replacing Array or directly re-wrapping
387
+ // .sort(..)...
378
388
  sortAs: function(other, place='head'){
379
389
  place = place == 'tail' ? -1 : 1
380
390
  // NOTE: the memory overhead here is better than the time overhead
@@ -441,8 +451,8 @@ object.Mixin('ArrayProtoMixin', 'soft', {
441
451
 
442
452
  // Convert an array to a map...
443
453
  //
444
- // This is similar to Array.prototype.toKeys(..) but does not restrict
445
- // value type to string.
454
+ // This is similar to Array.prototype.toKeys(..) but does not
455
+ // restrict value type to string.
446
456
  //
447
457
  // Format:
448
458
  // Map([
@@ -482,8 +492,22 @@ object.Mixin('ArrayProtoMixin', 'soft', {
482
492
  // -> iterator
483
493
  //
484
494
  // XXX should this take an argument and be like map??
485
- iter: function*(){
486
- yield* this },
495
+ // XXX this should handle throwing STOP!!!
496
+ // ...might also ne a good idea to isolate the STOP mechanics
497
+ // into a spearate module/package...
498
+ iter: stoppable(function*(handler=undefined){
499
+ if(handler){
500
+ var i = 0
501
+ for(var e of this){
502
+ var res = handler.call(this, e, i++)
503
+ // treat non-iterables as single elements...
504
+ if(typeof(res) == 'object'
505
+ && Symbol.iterator in res){
506
+ yield* res
507
+ } else {
508
+ yield res } }
509
+ } else {
510
+ yield* this }}),
487
511
 
488
512
 
489
513
  // Stoppable iteration...
package/Promise.js CHANGED
@@ -5,8 +5,7 @@
5
5
  * Promise.iter(seq)
6
6
  * <promise>.iter()
7
7
  * Iterable promise object.
8
- * Similar to Promise.all(..) but adds basic iterator/generator
9
- * API and will resolve the items as they are ready (resolved).
8
+ * Similar to Promise.all(..) but adds basic iterator API.
10
9
  *
11
10
  * Promise.interactive(handler)
12
11
  * Interactive promise object.
@@ -31,6 +30,8 @@
31
30
 
32
31
  var object = require('ig-object')
33
32
 
33
+ //var generator = require('./generator')
34
+
34
35
 
35
36
 
36
37
  //---------------------------------------------------------------------
@@ -39,16 +40,6 @@ var object = require('ig-object')
39
40
  // Like Promise.all(..) but adds ability to iterate through results
40
41
  // via generators .map(..)/.reduce(..) and friends...
41
42
  //
42
- // NOTE: it would be nice to support throwing STOP (XXX) from the iterable
43
- // promise but...
44
- // - this is more complicated than simply using .smap(..) instead
45
- // of .map(..) because the list can contain async promises...
46
- // ...would need to wrap each .then(..) call in try-catch and
47
- // manually handle the stop...
48
- // ...then there's a question of derivative iterators etc.
49
- // - another issue here is that the stop would happen in order of
50
- // execution and not order of elements...
51
- // XXX though stopping within a single level might be useful...
52
43
  // NOTE: the following can not be implemented here:
53
44
  // .splice(..) - can't both modify and return
54
45
  // a result...
@@ -68,18 +59,19 @@ var object = require('ig-object')
68
59
  // back to the complex IterablePromise...
69
60
  //
70
61
  // XXX how do we handle errors/rejections???
62
+ // ...mostly the current state is OK, but need more testing...
63
+ // XXX add support for async generators...
71
64
  //
72
65
 
73
- // XXX should these be exported???
74
66
  var iterPromiseProxy =
75
- //module.iterPromiseProxy =
67
+ module.iterPromiseProxy =
76
68
  function(name){
77
69
  return function(...args){
78
70
  return this.constructor(
79
71
  this.then(function(lst){
80
72
  return lst[name](...args) })) } }
81
73
  var promiseProxy =
82
- //module.promiseProxy =
74
+ module.promiseProxy =
83
75
  function(name){
84
76
  return async function(...args){
85
77
  return (await this)[name](...args) } }
@@ -87,6 +79,10 @@ function(name){
87
79
  var IterablePromise =
88
80
  module.IterablePromise =
89
81
  object.Constructor('IterablePromise', Promise, {
82
+ get STOP(){
83
+ return Array.STOP },
84
+
85
+ }, {
90
86
  // packed array...
91
87
  //
92
88
  // Holds promise state.
@@ -100,14 +96,14 @@ object.Constructor('IterablePromise', Promise, {
100
96
  // ]
101
97
  //
102
98
  // This format has several useful features:
103
- // - concatenating packed list results in a packed list
99
+ // - concatenating packed lists results in a packed list
104
100
  // - adding an iterable promise (as-is) into a packed list results
105
101
  // in a packed list
106
102
  //
107
103
  // 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.
104
+ // it is not recomended to ever edit this in-place...
105
+ // NOTE: we are not isolating or "protecting" any internals to
106
+ // enable users to responsibly extend the code.
111
107
  __packed: null,
112
108
 
113
109
  // low-level .__packed handlers/helpers...
@@ -115,6 +111,20 @@ object.Constructor('IterablePromise', Promise, {
115
111
  // NOTE: these can be useful for debugging and extending...
116
112
  //
117
113
  // pack and oprionally transform/handle an array (sync)...
114
+ //
115
+ // NOTE: if 'types/Array' is imported this will support throwing STOP
116
+ // from the handler.
117
+ // Due to the async nature of promises though the way stops are
118
+ // handled may be unpredictable -- the handlers can be run out
119
+ // of order, as the nested promises resolve and thus throwing
120
+ // stop will stop the handlers not yet run and not the next
121
+ // handlers in sequence.
122
+ // XXX EXPEREMENTAL: STOP...
123
+ // XXX add support for async generators...
124
+ // ...an async generator is not "parallel", i.e. intil one
125
+ // returned promise is resolved the generator blocks (will not
126
+ // advance)...
127
+ // ...can we feed out a results one by one???
118
128
  __pack: function(list, handler=undefined){
119
129
  var that = this
120
130
  // handle iterable promise list...
@@ -131,6 +141,57 @@ object.Constructor('IterablePromise', Promise, {
131
141
  handler = handler
132
142
  ?? function(elem){
133
143
  return [elem] }
144
+
145
+ //* XXX EXPEREMENTAL: STOP...
146
+ var stoppable = false
147
+ var stop = false
148
+ var map = 'map'
149
+ var pack = function(){
150
+ return [list].flat()
151
+ [map](function(elem){
152
+ return elem && elem.then ?
153
+ (stoppable ?
154
+ // stoppable -- need to handle stop async...
155
+ elem
156
+ .then(function(res){
157
+ return !stop ?
158
+ handler(res)
159
+ : [] })
160
+ // NOTE: we are using .catch(..) here
161
+ // instead of directly passing the
162
+ // error handler to be able to catch
163
+ // the STOP from the handler...
164
+ .catch(handleSTOP)
165
+ // non-stoppable...
166
+ : elem.then(handler))
167
+ : elem instanceof Array ?
168
+ handler(elem)
169
+ // NOTE: we keep things that do not need protecting
170
+ // from .flat() as-is...
171
+ : !handle ?
172
+ elem
173
+ : handler(elem) }) }
174
+
175
+ // pack (stoppable)...
176
+ if(!!this.constructor.STOP){
177
+ stoppable = true
178
+ map = 'smap'
179
+ var handleSTOP = function(err){
180
+ stop = err
181
+ if(err === that.constructor.STOP
182
+ || err instanceof that.constructor.STOP){
183
+ return 'value' in err ?
184
+ err.value
185
+ : [] }
186
+ throw err }
187
+ try{
188
+ return pack()
189
+ }catch(err){
190
+ return handleSTOP(err) } }
191
+
192
+ // pack (non-stoppable)...
193
+ return pack() },
194
+ /*/
134
195
  return [list].flat()
135
196
  .map(function(elem){
136
197
  return elem && elem.then ?
@@ -143,6 +204,7 @@ object.Constructor('IterablePromise', Promise, {
143
204
  : !handle ?
144
205
  elem
145
206
  : handler(elem) }) },
207
+ //*/
146
208
  // transform/handle packed array (sync)...
147
209
  __handle: function(list, handler=undefined){
148
210
  var that = this
@@ -159,13 +221,21 @@ object.Constructor('IterablePromise', Promise, {
159
221
  // NOTE: since each section of the packed .__array is the same
160
222
  // structure as the input we'll use .__pack(..) to handle
161
223
  // them, this also keeps all the handling code in one place.
224
+ //* XXX EXPEREMENTAL: STOP...
225
+ var map = !!this.constructor.STOP ?
226
+ 'smap'
227
+ : 'map'
228
+ return list[map](function(elem){
229
+ /*/
162
230
  return list.map(function(elem){
231
+ //*/
163
232
  return elem instanceof Array ?
164
233
  that.__pack(elem, handler)
165
234
  : elem instanceof Promise ?
166
235
  that.__pack(elem, handler)
167
- .then(function(elem){
168
- return elem.flat() })
236
+ //.then(function(elem){
237
+ .then(function([elem]){
238
+ return elem })
169
239
  : [handler(elem)] })
170
240
  .flat() },
171
241
  // unpack array (async)...
@@ -224,6 +294,8 @@ object.Constructor('IterablePromise', Promise, {
224
294
  .then(function(){
225
295
  return res }) },
226
296
 
297
+ // XXX .chain(..) -- see generator.chain(..)
298
+
227
299
  flat: function(depth=1){
228
300
  return this.constructor(this,
229
301
  function(e){
@@ -280,12 +352,6 @@ object.Constructor('IterablePromise', Promise, {
280
352
  return this.constructor([elem])
281
353
  .concat(this) },
282
354
 
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
355
  // proxy methods...
290
356
  //
291
357
  // These require the whole promise to resolve to trigger.
@@ -300,6 +366,7 @@ object.Constructor('IterablePromise', Promise, {
300
366
  var list = this.__packed
301
367
  return ((i != 0 && i != -1)
302
368
  || list instanceof Promise
369
+ // XXX not sure if this is correct...
303
370
  || list.at(i) instanceof Promise) ?
304
371
  (await this).at(i)
305
372
  // NOTE: we can only reason about first/last explicit elements,
@@ -316,29 +383,91 @@ 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.
319
- // XXX ???
386
+ // XXX is it faster to do .reverse().reduce(..) ???
320
387
  reduceRight: promiseProxy('reduceRight'),
321
388
 
322
389
  // 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...
390
+ // promises for values because any promise in .__packed makes
391
+ // the value count/index non-deterministic...
325
392
  sort: iterPromiseProxy('sort'),
326
- // XXX we could have a special-case here for .slice()/slice(0, -1)
327
- // and possibly othets, should we???
328
393
  slice: iterPromiseProxy('slice'),
329
394
 
330
395
  entries: iterPromiseProxy('entries'),
331
396
  keys: iterPromiseProxy('keys'),
332
- // XXX we could possibly make this better via .map(..)
333
397
  values: iterPromiseProxy('values'),
334
398
 
335
399
  indexOf: promiseProxy('indexOf'),
336
400
  lastIndexOf: promiseProxy('lastIndexOf'),
337
401
  includes: promiseProxy('includes'),
338
402
 
403
+ //
404
+ // .find(<func>)
405
+ // .find(<func>, 'value')
406
+ // -> <promise>(<value>)
407
+ //
408
+ // .find(<func>, 'result')
409
+ // -> <promise>(<result>)
410
+ //
411
+ // .find(<func>, 'bool')
412
+ // -> <promise>(<bool>)
413
+ //
414
+ // NOTE: this is slightly different to Array's .find(..) in that it
415
+ // accepts the result value enabling returning both the value
416
+ // itself ('value', default), the test function's result
417
+ // ('result') or true/false ('bool') -- this is added to be
418
+ // able to distinguish between the undefined as a stored value
419
+ // and undefined as a "nothing found" result.
420
+ // NOTE: I do not get how essentially identical methods .some(..)
421
+ // and .find(..) got added to JS's Array...
422
+ // the only benefit is that .some(..) handles undefined values
423
+ // stored in the array better...
424
+ // NOTE: this will return the result as soon as it's available but
425
+ // it will not stop the created but unresolved at the time
426
+ // promises from executing, this is both good and bad:
427
+ // + it will not break other clients waiting for promises
428
+ // to resolve...
429
+ // - if no clients are available this can lead to wasted
430
+ // CPU time...
431
+ find: async function(func, result='value'){
432
+ var that = this
433
+ // NOTE: not using pure await here as this is simpler to actually
434
+ // control the moment the resulting promise resolves without
435
+ // the need for juggling state...
436
+ return new Promise(function(resolve, reject){
437
+ var resolved = false
438
+ that.map(function(elem){
439
+ var res = func(elem)
440
+ if(res){
441
+ resolved = true
442
+ resolve(
443
+ result == 'bool' ?
444
+ true
445
+ : result == 'result' ?
446
+ res
447
+ : elem)
448
+ // XXX EXPEREMENTAL: STOP...
449
+ // NOTE: we do not need to throw STOP here
450
+ // but it can prevent some overhead...
451
+ if(that.constructor.STOP){
452
+ throw that.constructor.STOP } } })
453
+ .then(function(){
454
+ resolved
455
+ || resolve(
456
+ result == 'bool' ?
457
+ false
458
+ : undefined) }) }) },
459
+ findIndex: promiseProxy('findIndex'),
460
+
461
+ // NOTE: this is just a special-case of .find(..)
462
+ some: async function(func){
463
+ return this.find(func, 'bool') },
339
464
  every: promiseProxy('every'),
340
- // XXX this can be lazy... (???)
341
- some: promiseProxy('some'),
465
+
466
+
467
+ // this is defined globally as Promise.prototype.iter(..)
468
+ //
469
+ // for details see: PromiseMixin(..) below...
470
+ //iter: function(handler=undefined){ ... },
342
471
 
343
472
 
344
473
  // promise api...
@@ -368,12 +497,6 @@ object.Constructor('IterablePromise', Promise, {
368
497
  : p },
369
498
 
370
499
 
371
- // this is defined globally as Promise.prototype.iter(.,)
372
- //
373
- // for details see: PromiseMixin(..) below...
374
- //iter: function(handler=undefined){ ... },
375
-
376
-
377
500
  // constructor...
378
501
  //
379
502
  // Promise.iter([ .. ])
@@ -394,6 +517,7 @@ object.Constructor('IterablePromise', Promise, {
394
517
  // items...
395
518
  // XXX we can make the index a promise, then if the client needs
396
519
  // the value they can wait for it...
520
+ // ...this may be quite an overhead...
397
521
  //
398
522
  //
399
523
  // Special cases useful for extending this constructor...
@@ -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
@@ -526,6 +654,7 @@ object.Constructor('InteractivePromise', Promise, {
526
654
  // Cooperative promise...
527
655
  //
528
656
  // A promise that can be resolved/rejected externally.
657
+ //
529
658
  // NOTE: normally this has no internal resolver logic...
530
659
  //
531
660
 
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)
@@ -91,7 +94,7 @@ Library of JavaScript type extensions, types and utilities.
91
94
  - [`generator.iter(..)`](#generatoriter)
92
95
  - [`generator.STOP`](#generatorstop)
93
96
  - [Generator instance iteration](#generator-instance-iteration)
94
- - [`<generator>.iter()`](#generatoriter-1)
97
+ - [`<generator>.iter(..)`](#generatoriter-1)
95
98
  - [`<generator>.map(..)` / `<generator>.filter(..)`](#generatormap--generatorfilter)
96
99
  - [`<generator>.reduce(..)` / `<generator>.greduce(..)`](#generatorreduce--generatorgreduce)
97
100
  - [`<generator>.slice(..)`](#generatorslice)
@@ -99,7 +102,6 @@ Library of JavaScript type extensions, types and utilities.
99
102
  - [`<generator>.flat(..)`](#generatorflat)
100
103
  - [`<generator>.shift()` / `<generator>.pop()` / `<generator>.gshift()` / `<generator>.gpop()`](#generatorshift--generatorpop--generatorgshift--generatorgpop)
101
104
  - [`<generator>.unshift(..)` / `<generator>.push(..)`](#generatorunshift--generatorpush)
102
- - [`<generator>.promise()`](#generatorpromise)
103
105
  - [`<generator>.then(..)` / `<generator>.catch(..)` / `<generator>.finally(..)`](#generatorthen--generatorcatch--generatorfinally)
104
106
  - [`<generator>.toArray()`](#generatortoarray)
105
107
  - [Treating iterators the same as generators](#treating-iterators-the-same-as-generators)
@@ -121,6 +123,15 @@ Library of JavaScript type extensions, types and utilities.
121
123
  - [`generator.produce(..)`](#generatorproduce)
122
124
  - [Generator helpers](#generator-helpers)
123
125
  - [`generator.stoppable(..)`](#generatorstoppable)
126
+ - [Async generator extensions](#async-generator-extensions)
127
+ - [`generator.AsyncGenerator`](#generatorasyncgenerator)
128
+ - [`<async-generator>.then(..)` / `<async-generator>.catch(..)` / `<async-generator>.finally(..)`](#async-generatorthen--async-generatorcatch--async-generatorfinally)
129
+ - [`<async-generator>.iter(..)`](#async-generatoriter)
130
+ - [`<async-generator>.map(..)` / `<async-generator>.filter(..)` / `<async-generator>.reduce(..)`](#async-generatormap--async-generatorfilter--async-generatorreduce)
131
+ - [`<async-generator>.chain(..)`](#async-generatorchain)
132
+ - [`<async-generator>.flat(..)`](#async-generatorflat)
133
+ - [`<async-generator>.concat(..)`](#async-generatorconcat)
134
+ - [`<async-generator>.push(..)` / `<async-generator>.unshift(..)`](#async-generatorpush--async-generatorunshift)
124
135
  - [Containers](#containers)
125
136
  - [`containers.UniqueKeyMap()` (`Map`)](#containersuniquekeymap-map)
126
137
  - [`<unique-key-map>.set(..)`](#unique-key-mapset)
@@ -1674,6 +1685,46 @@ parent `<promise-iter>`.
1674
1685
 
1675
1686
  XXX
1676
1687
 
1688
+ #### `<promise-iter>.some(..)` / `<promise-iter>.find(..)`
1689
+
1690
+ ```bnf
1691
+ <promise-iter>.some(<func>)
1692
+ -> <promise>
1693
+
1694
+ <promise-iter>.find(<func>)
1695
+ -> <promise>
1696
+ ```
1697
+
1698
+ The main difference between `.some(..)` and `.find(..)` is in that the `<promise>`
1699
+ returned from the former will resolve to either `true` or `false`, and in the later
1700
+ to the found value or `undefined`.
1701
+
1702
+ `.find(..)` supports an additional argument that controls what returned `<promise>`
1703
+ is resolved to...
1704
+
1705
+ ```bnf
1706
+ <promise-iter>.find(<func>)
1707
+ <promise-iter>.find(<func>, 'value')
1708
+ -> <promise>
1709
+
1710
+ <promise-iter>.find(<func>, 'bool')
1711
+ -> <promise>
1712
+
1713
+ <promise-iter>.find(<func>, 'result')
1714
+ -> <promise>
1715
+ ```
1716
+
1717
+ - `value` (default)
1718
+ resolve to the stored value if found and `undefined` otherwise.
1719
+ - `bool`
1720
+ resolve to `true` if the value is found and `false` otherwise, this is how
1721
+ `.some(..)` is impelemnted.
1722
+ - `result`
1723
+ resolve to the return value of the test `<func>`.
1724
+
1725
+ These are similar to [`<array>.some(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
1726
+ and [`<array>.find(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
1727
+ see them for more info.
1677
1728
 
1678
1729
  #### Array proxy methods returning `<promise-iter>`
1679
1730
 
@@ -1697,7 +1748,8 @@ XXX links...
1697
1748
 
1698
1749
  - `<promise-iter>.indexOf(..)`
1699
1750
  - `<promise-iter>.includes(..)`
1700
- - `<promise-iter>.every(..)` / `<promise-iter>.some(..)`
1751
+ - `<promise-iter>.every(..)`
1752
+ - `<promise-iter>.findIndex(..)`
1701
1753
 
1702
1754
  These methods are proxies to the appropriate array methods.
1703
1755
 
@@ -1727,6 +1779,16 @@ this adds the ability to pass no arguments
1727
1779
  This will return a generic promise wrapper passing through the results as-is. This
1728
1780
  can be useful to hide the extended promise API from further code.
1729
1781
 
1782
+ #### `promise.IterablePromise.STOP` / `promise.IterablePromise.STOP(..)`
1783
+
1784
+ A special object that when thrown from a function/promise handler will stop
1785
+ further iteration.
1786
+
1787
+ This is `undefined` until the `ig-types/Array` module is loaded.
1788
+
1789
+ For more info see: [Stopping the iteration](#stopping-the-iteration) below, and
1790
+ [the 'Array' STOP section](#arraystop--arraystop)
1791
+
1730
1792
 
1731
1793
  #### Advanced handler
1732
1794
 
@@ -1778,6 +1840,24 @@ var p = Promise.iter(
1778
1840
  console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
1779
1841
  ```
1780
1842
 
1843
+ #### Stopping the iteration
1844
+
1845
+ Like the [`Array`](#arraystop--arraystop) module, this support throwing `STOP` to
1846
+ stop iteration. As we uses [`.smap(..)`](#arraysmap--arraysfilter--arraysreduce--arraysforeach)
1847
+ stopping support is supported if `ig-types/Array` module is loaded.
1848
+
1849
+ ```javascript
1850
+ require('ig-types/Array')
1851
+ ```
1852
+
1853
+ This is also different semantically, as promise iteration can happen out of order,
1854
+ 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`.
1855
+
1856
+ Any handler function passed to a `<promise-iter>` method can `throw` a STOP.
1857
+
1858
+ For more details see: [the 'Array' STOP section](#arraystop--arraystop)
1859
+
1860
+
1781
1861
 
1782
1862
  ### Promise proxies
1783
1863
 
@@ -1951,7 +2031,7 @@ Chained generators handle items depth-first, i.e. the items are passed as they
1951
2031
  are yielded down the generator chain.
1952
2032
 
1953
2033
 
1954
- #### `<generator>.iter()`
2034
+ #### `<generator>.iter(..)`
1955
2035
 
1956
2036
  Iterate over the generator.
1957
2037
  ```bnf
@@ -1959,7 +2039,24 @@ Iterate over the generator.
1959
2039
  -> <generator>
1960
2040
  ```
1961
2041
 
1962
- This is here mainly for compatibility with [`<array>`'s `.iter()`](#arrayiter--arrayiter).
2042
+ XXX move this to `generator.iter(..)`
2043
+
2044
+ Compatible with [`<array>`'s `.iter()`](#arrayiter--arrayiter).
2045
+
2046
+ `.iter(..)` also supports a handler function
2047
+ ```bnf
2048
+ <generator>.iter(<handler>)
2049
+ -> <generator>
2050
+
2051
+
2052
+ <handler>(<elem>, <index>)
2053
+ -> <elem>
2054
+ -> [<elem>, ..]
2055
+ -> []
2056
+ ```
2057
+
2058
+ Note that the iterables returned by `<handler>(..)` will be expanded, to prevent
2059
+ this wrap them in an array.
1963
2060
 
1964
2061
 
1965
2062
  #### `<generator>.map(..)` / `<generator>.filter(..)`
@@ -2084,30 +2181,29 @@ Value added by `.unshift(..)` will be yielded by `<generator>` "first", i.e. on
2084
2181
  _next_ call to `.next()`, regardless of the current generator state.
2085
2182
 
2086
2183
 
2087
- #### `<generator>.promise()`
2184
+ #### `<generator>.then(..)` / `<generator>.catch(..)` / `<generator>.finally(..)`
2185
+
2186
+ Return a promise and resolve it with the generator value.
2088
2187
 
2089
2188
  ```bnf
2090
- <generator>.promise()
2189
+ <generator>.then()
2091
2190
  -> <promise>
2092
2191
  ```
2093
- Return a promise and resolve it with the generator value.
2094
2192
 
2095
- Note that this will deplete the generator.
2096
-
2097
-
2098
- #### `<generator>.then(..)` / `<generator>.catch(..)` / `<generator>.finally(..)`
2193
+ Adding handlers to the promise
2099
2194
 
2100
2195
  ```bnf
2101
2196
  <generator>.then(<resolve>, <reject>)
2102
2197
  -> <promise>
2103
2198
 
2104
- <generator>.then(<reject>)
2199
+ <generator>.then(<resolve>)
2105
2200
  -> <promise>
2106
2201
 
2107
2202
  <generator>.finally(<handler>)
2108
2203
  -> <promise>
2109
2204
  ```
2110
- Shorthands to `<generator>.promise().then(..)` / `<generator>.promise().catch(..)` / `<generator>.promise().finally(..)`
2205
+
2206
+ Note that this will deplete the generator.
2111
2207
 
2112
2208
  These are the same as equivalent `Promise` methods, for more info see:
2113
2209
  https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
@@ -2411,6 +2507,26 @@ stoppable(<generator>)
2411
2507
 
2412
2508
  <!-- XXX example? -->
2413
2509
 
2510
+ ## Async generator extensions
2511
+
2512
+ XXX EXPERIMENTAL
2513
+
2514
+ #### `generator.AsyncGenerator`
2515
+
2516
+ #### `<async-generator>.then(..)` / `<async-generator>.catch(..)` / `<async-generator>.finally(..)`
2517
+
2518
+ #### `<async-generator>.iter(..)`
2519
+
2520
+ #### `<async-generator>.map(..)` / `<async-generator>.filter(..)` / `<async-generator>.reduce(..)`
2521
+
2522
+ #### `<async-generator>.chain(..)`
2523
+
2524
+ #### `<async-generator>.flat(..)`
2525
+
2526
+ #### `<async-generator>.concat(..)`
2527
+
2528
+ #### `<async-generator>.push(..)` / `<async-generator>.unshift(..)`
2529
+
2414
2530
 
2415
2531
 
2416
2532
  ## Containers
package/generator.js CHANGED
@@ -66,6 +66,14 @@ module.Generator =
66
66
  (function*(){}).constructor
67
67
 
68
68
 
69
+ var AsyncGeneratorPrototype =
70
+ (async function*(){}).constructor.prototype
71
+
72
+ var AsyncGenerator =
73
+ module.AsyncGenerator =
74
+ (async function*(){}).constructor
75
+
76
+
69
77
  // base iterator prototypes...
70
78
  var ITERATOR_PROTOTYPES = [
71
79
  Array,
@@ -78,13 +86,86 @@ var ITERATOR_PROTOTYPES = [
78
86
 
79
87
  //---------------------------------------------------------------------
80
88
 
89
+ // XXX should this be part of object???
90
+ var stoppable =
91
+ module.stoppable =
92
+ function(func){
93
+ return Object.assign(
94
+ func instanceof Generator ?
95
+ // NOTE: the only difference between Generator/AsyncGenerator
96
+ // versions of this is the async keyword -- keep them
97
+ // in sync...
98
+ function*(){
99
+ try{
100
+ yield* func.call(this, ...arguments)
101
+ } catch(err){
102
+ if(err === STOP){
103
+ return
104
+ } else if(err instanceof STOP){
105
+ yield err.value
106
+ return }
107
+ throw err } }
108
+ : func instanceof AsyncGenerator ?
109
+ // NOTE: the only difference between Generator/AsyncGenerator
110
+ // versions of this is the async keyword -- keep them
111
+ // in sync...
112
+ async function*(){
113
+ try{
114
+ yield* func.call(this, ...arguments)
115
+ } catch(err){
116
+ if(err === STOP){
117
+ return
118
+ } else if(err instanceof STOP){
119
+ yield err.value
120
+ return }
121
+ throw err } }
122
+ : function(){
123
+ try{
124
+ return func.call(this, ...arguments)
125
+ } catch(err){
126
+ if(err === STOP){
127
+ return
128
+ } else if(err instanceof STOP){
129
+ return err.value }
130
+ throw err } },
131
+ { toString: function(){
132
+ return func.toString() }, }) }
133
+
134
+
135
+ //---------------------------------------------------------------------
81
136
  // generic generator wrapper...
137
+
138
+ // helper...
139
+ var __iter =
140
+ module.__iter =
141
+ function*(lst=[]){
142
+ if(typeof(lst) == 'object'
143
+ && Symbol.iterator in lst){
144
+ yield* lst
145
+ } else {
146
+ yield lst } }
147
+
148
+ // XXX updatae Array.js' version for compatibility...
149
+ // XXX DOCS!!!
82
150
  var iter =
83
151
  module.iter =
84
152
  Generator.iter =
85
- function*(lst=[]){
86
- for(var e of lst){
87
- yield e } }
153
+ stoppable(function(lst=[]){
154
+ // handler -> generator-constructor...
155
+ if(typeof(lst) == 'function'){
156
+ // we need to be callable...
157
+ var that = this instanceof Function ?
158
+ this
159
+ // generic root generator...
160
+ : module.__iter
161
+ return function*(){
162
+ yield* that(...arguments).iter(lst) } }
163
+ // no handler -> generator instance...
164
+ return module.__iter(lst) })
165
+
166
+ // NOTE: we need .iter(..) to both return generators if passed an iterable
167
+ // and genereator constructos if passed a function...
168
+ iter.__proto__ = GeneratorPrototype
88
169
 
89
170
 
90
171
 
@@ -113,9 +194,11 @@ Generator.iter =
113
194
 
114
195
  //
115
196
  // makeGenerator(<name>)
197
+ // makeGenerator(<name>, <handler>)
116
198
  // -> <func>
117
199
  //
118
- // makeGenerator(<name>, <handler>)
200
+ // makeGenerator('async', <name>)
201
+ // makeGenerator('async', <name>, <handler>)
119
202
  // -> <func>
120
203
  //
121
204
  //
@@ -132,14 +215,26 @@ Generator.iter =
132
215
  // XXX this needs to be of the correct type... (???)
133
216
  // XXX need to accept generators as handlers...
134
217
  var makeGenerator = function(name, pre){
218
+ var sync = true
219
+ if(name == 'async'){
220
+ sync = false
221
+ var [name, pre] = [...arguments].slice(1) }
135
222
  return function(...args){
136
223
  var that = this
137
224
  return Object.assign(
138
- function*(){
139
- var a = pre ?
140
- pre.call(this, args, ...arguments)
141
- : args
142
- yield* that(...arguments)[name](...a) },
225
+ // NOTE: the two branches here are identical, the only
226
+ // difference is the async keyword...
227
+ sync ?
228
+ function*(){
229
+ var a = pre ?
230
+ pre.call(this, args, ...arguments)
231
+ : args
232
+ yield* that(...arguments)[name](...a) }
233
+ : async function*(){
234
+ var a = pre ?
235
+ pre.call(this, args, ...arguments)
236
+ : args
237
+ yield* that(...arguments)[name](...a) },
143
238
  { toString: function(){
144
239
  return [
145
240
  that.toString(),
@@ -155,34 +250,6 @@ var makePromise = function(name){
155
250
  return that(...arguments)[name](func) } } }
156
251
 
157
252
 
158
- var stoppable =
159
- module.stoppable =
160
- function(func){
161
- return Object.assign(
162
- func instanceof Generator ?
163
- function*(){
164
- try{
165
- yield* func.call(this, ...arguments)
166
- } catch(err){
167
- if(err === STOP){
168
- return
169
- } else if(err instanceof STOP){
170
- yield err.value
171
- return }
172
- throw err } }
173
- : function(){
174
- try{
175
- return func.call(this, ...arguments)
176
- } catch(err){
177
- if(err === STOP){
178
- return
179
- } else if(err instanceof STOP){
180
- return err.value }
181
- throw err } },
182
- { toString: function(){
183
- return func.toString() }, }) }
184
-
185
-
186
253
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
187
254
 
188
255
  var GeneratorMixin =
@@ -190,9 +257,7 @@ module.GeneratorMixin =
190
257
  object.Mixin('GeneratorMixin', 'soft', {
191
258
  STOP: object.STOP,
192
259
 
193
- // NOTE: this is here for compatibility with Array.iter(..)
194
- iter: function*(lst=[]){
195
- yield* module.iter(lst) },
260
+ iter: module.iter,
196
261
 
197
262
  gat: makeGenerator('gat'),
198
263
  at: function(i){
@@ -264,15 +329,31 @@ object.Mixin('GeneratorMixin', 'soft', {
264
329
  e(...args)
265
330
  : e }) }),
266
331
  //zip: makeGenerator('zip'),
332
+
333
+ enumerate: makeGenerator('enumerate'),
267
334
  })
268
335
 
269
336
 
270
337
  var GeneratorProtoMixin =
271
338
  module.GeneratorProtoMixin =
272
339
  object.Mixin('GeneratorProtoMixin', 'soft', {
273
- // NOTE: this is here for compatibility with [..].iter()
274
- iter: function*(){
275
- yield* this },
340
+ // XXX use module.iter(..) ???
341
+ iter: stoppable(function*(handler){
342
+ if(handler){
343
+ var i = 0
344
+ for(var elem of this){
345
+ var res = handler.call(this, elem, i)
346
+ // expand iterables...
347
+ if(typeof(res) == 'object'
348
+ && Symbol.iterator in res){
349
+ yield* res
350
+ // as-is...
351
+ } else {
352
+ yield res }}
353
+ // no handler...
354
+ } else {
355
+ yield* this } }),
356
+ //*/
276
357
 
277
358
  at: function(i){
278
359
  return this.gat(i).next().value },
@@ -382,17 +463,19 @@ object.Mixin('GeneratorProtoMixin', 'soft', {
382
463
 
383
464
  // promises...
384
465
  //
385
- // XXX how do we handle reject(..) / .catch(..)???
386
- promise: function(){
466
+ then: function(onresolve, onreject){
387
467
  var that = this
388
- return new Promise(function(resolve){
389
- resolve([...that]) }) },
390
- then: function(func){
391
- return this.promise().then(func) },
468
+ var p = new Promise(
469
+ function(resolve){
470
+ resolve([...that]) })
471
+ p = (onresolve || onreject) ?
472
+ p.then(...arguments)
473
+ : p
474
+ return p },
392
475
  catch: function(func){
393
- return this.promise().catch(func) },
476
+ return this.then().catch(func) },
394
477
  finally: function(func){
395
- return this.promise().finally(func) },
478
+ return this.then().finally(func) },
396
479
 
397
480
  // combinators...
398
481
  //
@@ -415,6 +498,11 @@ object.Mixin('GeneratorProtoMixin', 'soft', {
415
498
  // XXX
416
499
  },
417
500
  //*/
501
+
502
+ enumerate: function*(){
503
+ var i = 0
504
+ for(var e of this){
505
+ yield [i++, e] } },
418
506
  })
419
507
 
420
508
 
@@ -428,6 +516,107 @@ ITERATOR_PROTOTYPES
428
516
  GeneratorProtoMixin(proto) })
429
517
 
430
518
 
519
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
520
+ // XXX EXPERIMENTAL...
521
+
522
+ var AsyncGeneratorMixin =
523
+ module.AsyncGeneratorMixin =
524
+ object.Mixin('AsyncGeneratorMixin', 'soft', {
525
+ // XXX TEST...
526
+ iter: makeGenerator('async', 'iter'),
527
+
528
+ map: makeGenerator('async', 'map'),
529
+ filter: makeGenerator('async', 'filter'),
530
+ reduce: makeGenerator('async', 'reduce'),
531
+ })
532
+
533
+ var AsyncGeneratorProtoMixin =
534
+ module.AsyncGeneratorProtoMixin =
535
+ object.Mixin('AsyncGeneratorProtoMixin', 'soft', {
536
+ // promise...
537
+ //
538
+ // NOTE: this will unwind the generator...
539
+ // XXX create an iterator promise???
540
+ // XXX should we unwind???
541
+ then: function(resolve, reject){
542
+ var that = this
543
+ var p = new Promise(async function(_resolve, _reject){
544
+ var res = []
545
+ for await(var elem of that){
546
+ res.push(elem) }
547
+ _resolve(res) })
548
+ p = (resolve || reject) ?
549
+ p.then(...arguments)
550
+ : p
551
+ return p },
552
+ catch: function(func){
553
+ return this.then().catch(func) },
554
+ finally: function(){
555
+ return this.then().finally(func) },
556
+
557
+ // XXX might be a good idea to use this approach above...
558
+ iter: stoppable(async function*(handler=undefined){
559
+ var i = 0
560
+ if(handler){
561
+ for await(var e of this){
562
+ var res = handler.call(this, e, i++)
563
+ if(typeof(res) == 'object'
564
+ && Symbol.iterator in res){
565
+ yield* res
566
+ } else {
567
+ yield res } }
568
+ } else {
569
+ yield* this } }),
570
+
571
+ map: async function*(func){
572
+ yield* this.iter(function(elem, i){
573
+ return [func.call(this, elem, i)] }) },
574
+ filter: async function*(func){
575
+ yield* this.iter(function(elem, i){
576
+ return func.call(this, elem, i) ?
577
+ [elem]
578
+ : [] }) },
579
+ // NOTE: there is not much point in .reduceRight(..) in an async
580
+ // generator as we'll need to fully unwind it then go from the
581
+ // end...
582
+ reduce: async function(func, state){
583
+ this.iter(function(elem, i){
584
+ state = func.call(this, state, elem, i)
585
+ return [] })
586
+ return state },
587
+
588
+ // XXX TEST...
589
+ chain: async function*(...next){
590
+ yield* next
591
+ .reduce(function(cur, next){
592
+ return next(cur) }, this) },
593
+
594
+ flat: async function*(){
595
+ for await(var e of this){
596
+ if(e instanceof Array){
597
+ yield* e
598
+ } else {
599
+ yield e }}},
600
+
601
+ concat: async function*(other){
602
+ yield* this
603
+ yield* other },
604
+ push: async function*(elem){
605
+ yield* this
606
+ yield elem },
607
+ unsift: async function*(elem){
608
+ yield elem
609
+ yield* this },
610
+
611
+ // XXX
612
+ // slice -- not sure if we need this...
613
+ // ...
614
+ })
615
+
616
+ AsyncGeneratorMixin(AsyncGeneratorPrototype)
617
+ AsyncGeneratorProtoMixin(AsyncGeneratorPrototype.prototype)
618
+
619
+
431
620
 
432
621
  //---------------------------------------------------------------------
433
622
  // Generators...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ig-types",
3
- "version": "6.14.1",
3
+ "version": "6.15.6",
4
4
  "description": "Generic JavaScript types and type extensions...",
5
5
  "main": "main.js",
6
6
  "scripts": {