ig-types 6.14.0 → 6.15.1
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.
- package/Array.js +19 -13
- package/Promise.js +265 -100
- package/README.md +123 -0
- package/generator.js +43 -0
- 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())
|
|
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
|
|
323
|
-
|
|
324
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
377
|
-
// and replacing Array or directly re-wrapping
|
|
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
|
|
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
|
@@ -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
|
|
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.
|
|
@@ -39,31 +38,37 @@ var object = require('ig-object')
|
|
|
39
38
|
// Like Promise.all(..) but adds ability to iterate through results
|
|
40
39
|
// via generators .map(..)/.reduce(..) and friends...
|
|
41
40
|
//
|
|
42
|
-
// NOTE:
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
// XXX though stopping within a single level might be useful...
|
|
52
|
-
//
|
|
53
|
-
|
|
41
|
+
// NOTE: the following can not be implemented here:
|
|
42
|
+
// .splice(..) - can't both modify and return
|
|
43
|
+
// a result...
|
|
44
|
+
// .pop() / .shift() - can't modify the promise, use
|
|
45
|
+
// .first() / .last() instead.
|
|
46
|
+
// [Symbol.iterator]() - needs to be sync and we can't
|
|
47
|
+
// know the number of elements to
|
|
48
|
+
// return promises before the whole
|
|
49
|
+
// iterable promise is resolved.
|
|
54
50
|
// NOTE: we are not using async/await here as we need to control the
|
|
55
51
|
// type of promise returned in cases where we know we are returning
|
|
56
52
|
// an array...
|
|
57
|
-
//
|
|
53
|
+
// NOTE: there is no point in implementing a 1:1 version of this that
|
|
54
|
+
// would not support element expansion/contraction as it would only
|
|
55
|
+
// simplify a couple of methods that are 1:1 (like .map(..) and
|
|
56
|
+
// .some(..)) while methods like .filter(..) will throw everything
|
|
57
|
+
// back to the complex IterablePromise...
|
|
58
|
+
//
|
|
59
|
+
// XXX how do we handle errors/rejections???
|
|
60
|
+
// ...mostly the current state is OK, but need more testing...
|
|
61
|
+
//
|
|
62
|
+
|
|
58
63
|
var iterPromiseProxy =
|
|
59
|
-
|
|
64
|
+
module.iterPromiseProxy =
|
|
60
65
|
function(name){
|
|
61
66
|
return function(...args){
|
|
62
67
|
return this.constructor(
|
|
63
68
|
this.then(function(lst){
|
|
64
69
|
return lst[name](...args) })) } }
|
|
65
70
|
var promiseProxy =
|
|
66
|
-
|
|
71
|
+
module.promiseProxy =
|
|
67
72
|
function(name){
|
|
68
73
|
return async function(...args){
|
|
69
74
|
return (await this)[name](...args) } }
|
|
@@ -71,6 +76,13 @@ function(name){
|
|
|
71
76
|
var IterablePromise =
|
|
72
77
|
module.IterablePromise =
|
|
73
78
|
object.Constructor('IterablePromise', Promise, {
|
|
79
|
+
get STOP(){
|
|
80
|
+
return Array.STOP },
|
|
81
|
+
|
|
82
|
+
}, {
|
|
83
|
+
// packed array...
|
|
84
|
+
//
|
|
85
|
+
// Holds promise state.
|
|
74
86
|
//
|
|
75
87
|
// Format:
|
|
76
88
|
// [
|
|
@@ -81,22 +93,35 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
81
93
|
// ]
|
|
82
94
|
//
|
|
83
95
|
// This format has several useful features:
|
|
84
|
-
// - concatenating packed
|
|
96
|
+
// - concatenating packed lists results in a packed list
|
|
85
97
|
// - adding an iterable promise (as-is) into a packed list results
|
|
86
98
|
// in a packed list
|
|
87
99
|
//
|
|
88
100
|
// NOTE: in general iterable promises are implicitly immutable, so
|
|
89
|
-
// it is not recomended to ever edit this
|
|
90
|
-
|
|
101
|
+
// it is not recomended to ever edit this in-place...
|
|
102
|
+
// NOTE: we are not isolating or "protecting" any internals to
|
|
103
|
+
// enable users to responsibly extend the code.
|
|
104
|
+
__packed: null,
|
|
91
105
|
|
|
92
|
-
// low-level .
|
|
106
|
+
// low-level .__packed handlers/helpers...
|
|
93
107
|
//
|
|
94
108
|
// NOTE: these can be useful for debugging and extending...
|
|
95
|
-
|
|
109
|
+
//
|
|
110
|
+
// pack and oprionally transform/handle an array (sync)...
|
|
111
|
+
//
|
|
112
|
+
// NOTE: if 'types/Array' is imported this will support throwing STOP
|
|
113
|
+
// from the handler.
|
|
114
|
+
// Due to the async nature of promises though the way stops are
|
|
115
|
+
// handled may be unpredictable -- the handlers can be run out
|
|
116
|
+
// of order, as the nested promises resolve and thus throwing
|
|
117
|
+
// stop will stop the handlers not yet run and not the next
|
|
118
|
+
// handlers in sequence.
|
|
119
|
+
// XXX EXPEREMENTAL: STOP...
|
|
120
|
+
__pack: function(list, handler=undefined){
|
|
96
121
|
var that = this
|
|
97
122
|
// handle iterable promise list...
|
|
98
123
|
if(list instanceof IterablePromise){
|
|
99
|
-
return this.__handle(list.
|
|
124
|
+
return this.__handle(list.__packed, handler) }
|
|
100
125
|
// handle promise list...
|
|
101
126
|
if(list instanceof Promise){
|
|
102
127
|
return list.then(function(list){
|
|
@@ -108,6 +133,57 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
108
133
|
handler = handler
|
|
109
134
|
?? function(elem){
|
|
110
135
|
return [elem] }
|
|
136
|
+
|
|
137
|
+
//* XXX EXPEREMENTAL: STOP...
|
|
138
|
+
var stoppable = false
|
|
139
|
+
var stop = false
|
|
140
|
+
var map = 'map'
|
|
141
|
+
var pack = function(){
|
|
142
|
+
return [list].flat()
|
|
143
|
+
[map](function(elem){
|
|
144
|
+
return elem && elem.then ?
|
|
145
|
+
(stoppable ?
|
|
146
|
+
// stoppable -- need to handle stop async...
|
|
147
|
+
elem
|
|
148
|
+
.then(function(res){
|
|
149
|
+
return !stop ?
|
|
150
|
+
handler(res)
|
|
151
|
+
: [] })
|
|
152
|
+
// NOTE: we are using .catch(..) here
|
|
153
|
+
// instead of directly passing the
|
|
154
|
+
// error handler to be able to catch
|
|
155
|
+
// the STOP from the handler...
|
|
156
|
+
.catch(handleSTOP)
|
|
157
|
+
// non-stoppable...
|
|
158
|
+
: elem.then(handler))
|
|
159
|
+
: elem instanceof Array ?
|
|
160
|
+
handler(elem)
|
|
161
|
+
// NOTE: we keep things that do not need protecting
|
|
162
|
+
// from .flat() as-is...
|
|
163
|
+
: !handle ?
|
|
164
|
+
elem
|
|
165
|
+
: handler(elem) }) }
|
|
166
|
+
|
|
167
|
+
// pack (stoppable)...
|
|
168
|
+
if(!!this.constructor.STOP){
|
|
169
|
+
stoppable = true
|
|
170
|
+
map = 'smap'
|
|
171
|
+
var handleSTOP = function(err){
|
|
172
|
+
stop = err
|
|
173
|
+
if(err === that.constructor.STOP
|
|
174
|
+
|| err instanceof that.constructor.STOP){
|
|
175
|
+
return 'value' in err ?
|
|
176
|
+
err.value
|
|
177
|
+
: [] }
|
|
178
|
+
throw err }
|
|
179
|
+
try{
|
|
180
|
+
return pack()
|
|
181
|
+
}catch(err){
|
|
182
|
+
return handleSTOP(err) } }
|
|
183
|
+
|
|
184
|
+
// pack (non-stoppable)...
|
|
185
|
+
return pack() },
|
|
186
|
+
/*/
|
|
111
187
|
return [list].flat()
|
|
112
188
|
.map(function(elem){
|
|
113
189
|
return elem && elem.then ?
|
|
@@ -120,11 +196,27 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
120
196
|
: !handle ?
|
|
121
197
|
elem
|
|
122
198
|
: handler(elem) }) },
|
|
123
|
-
|
|
199
|
+
//*/
|
|
200
|
+
// transform/handle packed array (sync)...
|
|
201
|
+
//
|
|
202
|
+
// XXX BUG:
|
|
203
|
+
// await Promise.iter(['a', Promise.resolve(222), ['c']])
|
|
204
|
+
// .map(async e => e)
|
|
205
|
+
// -> ['a', <promise>, ['c']]
|
|
206
|
+
// is not the same as:
|
|
207
|
+
// await Promise.iter(['a', Promise.resolve(222), ['c']])
|
|
208
|
+
// .map(e => e)
|
|
209
|
+
// -> ['a', 222, ['c']]
|
|
210
|
+
// ...and these works as expected:
|
|
211
|
+
// await Promise.iter(['a', Promise.resolve(222), ['c']], async e => [e])
|
|
212
|
+
// -> ['a', 222, ['c']]
|
|
213
|
+
// await Promise.iter(['a', Promise.resolve(222), ['c']], e => [e])
|
|
214
|
+
// -> ['a', 222, ['c']]
|
|
215
|
+
__handle: function(list, handler=undefined){
|
|
124
216
|
var that = this
|
|
125
217
|
if(typeof(list) == 'function'){
|
|
126
218
|
handler = list
|
|
127
|
-
list = this.
|
|
219
|
+
list = this.__packed }
|
|
128
220
|
if(!handler){
|
|
129
221
|
return list }
|
|
130
222
|
// handle promise list...
|
|
@@ -135,58 +227,45 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
135
227
|
// NOTE: since each section of the packed .__array is the same
|
|
136
228
|
// structure as the input we'll use .__pack(..) to handle
|
|
137
229
|
// them, this also keeps all the handling code in one place.
|
|
230
|
+
//* XXX EXPEREMENTAL: STOP...
|
|
231
|
+
var map = !!this.constructor.STOP ?
|
|
232
|
+
'smap'
|
|
233
|
+
: 'map'
|
|
234
|
+
return list[map](function(elem){
|
|
235
|
+
/*/
|
|
138
236
|
return list.map(function(elem){
|
|
237
|
+
//*/
|
|
139
238
|
return elem instanceof Array ?
|
|
140
239
|
that.__pack(elem, handler)
|
|
141
240
|
: elem instanceof Promise ?
|
|
142
241
|
that.__pack(elem, handler)
|
|
143
|
-
|
|
144
|
-
|
|
242
|
+
//.then(function(elem){
|
|
243
|
+
.then(function([elem]){
|
|
244
|
+
return elem })
|
|
145
245
|
: [handler(elem)] })
|
|
146
246
|
.flat() },
|
|
147
|
-
|
|
247
|
+
// unpack array (async)...
|
|
248
|
+
__unpack: async function(list){
|
|
148
249
|
list = list
|
|
149
|
-
?? this.
|
|
250
|
+
?? this.__packed
|
|
150
251
|
// handle promise list...
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return Promise.all(list)
|
|
157
|
-
.then(function(list){
|
|
158
|
-
return list.flat() }) },
|
|
252
|
+
return list instanceof Promise ?
|
|
253
|
+
this.__unpack(await list)
|
|
254
|
+
// do the work...
|
|
255
|
+
: (await Promise.all(list))
|
|
256
|
+
.flat() },
|
|
159
257
|
|
|
160
258
|
|
|
161
259
|
// iterator methods...
|
|
162
260
|
//
|
|
163
261
|
// These will return a new IterablePromise instance...
|
|
164
262
|
//
|
|
165
|
-
// When called from a resolved promise these will return a new
|
|
166
|
-
// resolved promise with updated values...
|
|
167
|
-
//
|
|
168
|
-
// When called from a rejected promise these will return a rejected
|
|
169
|
-
// with the same reason promise...
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
263
|
// NOTE: these are different to Array's equivalents in that the handler
|
|
173
264
|
// is called not in the order of the elements but rather in order
|
|
174
265
|
// of promise resolution...
|
|
175
266
|
// NOTE: index of items is unknowable because items can expand and
|
|
176
|
-
// contract depending on
|
|
267
|
+
// contract depending on handlers (e.g. .filter(..) can remove
|
|
177
268
|
// items)...
|
|
178
|
-
// This the following can not be implemented here:
|
|
179
|
-
// .slice(..)
|
|
180
|
-
// .splice(..)
|
|
181
|
-
// .values() / .keys()
|
|
182
|
-
// .at(..)
|
|
183
|
-
// [Symbol.iterator]() - needs to be sync...
|
|
184
|
-
// The followng methods are questionable:
|
|
185
|
-
// .indexOf(..)
|
|
186
|
-
// .includes(..)
|
|
187
|
-
// .some(..) / .every(..)
|
|
188
|
-
// .sort(..)
|
|
189
|
-
// XXX update this note...
|
|
190
269
|
map: function(func){
|
|
191
270
|
return this.constructor(this,
|
|
192
271
|
function(e){
|
|
@@ -208,11 +287,11 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
208
287
|
: _filter(e) }) },
|
|
209
288
|
// NOTE: this does not return an iterable promise as we can't know
|
|
210
289
|
// what the user reduces to...
|
|
211
|
-
// XXX we could look at the initial state though...
|
|
212
290
|
// NOTE: the items can be handled out of order because the nested
|
|
213
291
|
// promises can resolve in any order...
|
|
214
292
|
// NOTE: since order of execution can not be guaranteed there is no
|
|
215
|
-
// point in implementing .reduceRight(..)
|
|
293
|
+
// point in implementing .reduceRight(..) in the same way
|
|
294
|
+
// (see below)...
|
|
216
295
|
reduce: function(func, res){
|
|
217
296
|
return this.constructor(this,
|
|
218
297
|
function(e){
|
|
@@ -232,7 +311,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
232
311
|
e
|
|
233
312
|
: [e] }) },
|
|
234
313
|
reverse: function(){
|
|
235
|
-
var lst = this.
|
|
314
|
+
var lst = this.__packed
|
|
236
315
|
return this.constructor(
|
|
237
316
|
lst instanceof Promise ?
|
|
238
317
|
lst.then(function(elems){
|
|
@@ -277,23 +356,21 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
277
356
|
return this.constructor([elem])
|
|
278
357
|
.concat(this) },
|
|
279
358
|
|
|
280
|
-
//
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
// NOTE: this can avoid waiting for the whole promise to resolve only
|
|
291
|
-
// for indexes 0 and -1, everything else is non-deterministic and
|
|
292
|
-
// we'll have to wait for the whole thing to resolve.
|
|
359
|
+
// proxy methods...
|
|
360
|
+
//
|
|
361
|
+
// These require the whole promise to resolve to trigger.
|
|
362
|
+
//
|
|
363
|
+
// An exception to this would be .at(0)/.first() and .at(-1)/.last()
|
|
364
|
+
// that can get the target element if it's accessible.
|
|
365
|
+
//
|
|
366
|
+
// NOTE: methods that are guaranteed to return an array will return
|
|
367
|
+
// an iterable promise (created with iterPromiseProxy(..))...
|
|
368
|
+
//
|
|
293
369
|
at: async function(i){
|
|
294
|
-
var list = this.
|
|
370
|
+
var list = this.__packed
|
|
295
371
|
return ((i != 0 && i != -1)
|
|
296
372
|
|| list instanceof Promise
|
|
373
|
+
// XXX not sure if this is correct...
|
|
297
374
|
|| list.at(i) instanceof Promise) ?
|
|
298
375
|
(await this).at(i)
|
|
299
376
|
// NOTE: we can only reason about first/last explicit elements,
|
|
@@ -308,24 +385,97 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
308
385
|
last: function(){
|
|
309
386
|
return this.at(-1) },
|
|
310
387
|
|
|
388
|
+
// NOTE: unlike .reduce(..) this needs the parent fully resolved
|
|
389
|
+
// to be able to iterate from the end.
|
|
390
|
+
// XXX is it faster to do .reverse().reduce(..) ???
|
|
391
|
+
reduceRight: promiseProxy('reduceRight'),
|
|
392
|
+
|
|
311
393
|
// NOTE: there is no way we can do a sync generator returning
|
|
312
|
-
// promises for values because any promise in .
|
|
313
|
-
// value count/index non-deterministic...
|
|
394
|
+
// promises for values because any promise in .__packed makes
|
|
395
|
+
// the value count/index non-deterministic...
|
|
314
396
|
sort: iterPromiseProxy('sort'),
|
|
315
397
|
slice: iterPromiseProxy('slice'),
|
|
398
|
+
|
|
316
399
|
entries: iterPromiseProxy('entries'),
|
|
317
400
|
keys: iterPromiseProxy('keys'),
|
|
318
|
-
// XXX we could possibly make this better via .map(..)
|
|
319
401
|
values: iterPromiseProxy('values'),
|
|
320
402
|
|
|
321
403
|
indexOf: promiseProxy('indexOf'),
|
|
404
|
+
lastIndexOf: promiseProxy('lastIndexOf'),
|
|
322
405
|
includes: promiseProxy('includes'),
|
|
323
406
|
|
|
407
|
+
//
|
|
408
|
+
// .find(<func>)
|
|
409
|
+
// .find(<func>, 'value')
|
|
410
|
+
// -> <promise>(<value>)
|
|
411
|
+
//
|
|
412
|
+
// .find(<func>, 'result')
|
|
413
|
+
// -> <promise>(<result>)
|
|
414
|
+
//
|
|
415
|
+
// .find(<func>, 'bool')
|
|
416
|
+
// -> <promise>(<bool>)
|
|
417
|
+
//
|
|
418
|
+
// NOTE: this is slightly different to Array's .find(..) in that it
|
|
419
|
+
// accepts the result value enabling returning both the value
|
|
420
|
+
// itself ('value', default), the test function's result
|
|
421
|
+
// ('result') or true/false ('bool') -- this is added to be
|
|
422
|
+
// able to distinguish between the undefined as a stored value
|
|
423
|
+
// and undefined as a "nothing found" result.
|
|
424
|
+
// NOTE: I do not get how essentially identical methods .some(..)
|
|
425
|
+
// and .find(..) got added to JS's Array...
|
|
426
|
+
// the only benefit is that .some(..) handles undefined values
|
|
427
|
+
// stored in the array better...
|
|
428
|
+
// NOTE: this will return the result as soon as it's available but
|
|
429
|
+
// it will not stop the created but unresolved at the time
|
|
430
|
+
// promises from executing, this is both good and bad:
|
|
431
|
+
// + it will not break other clients waiting for promises
|
|
432
|
+
// to resolve...
|
|
433
|
+
// - if no clients are available this can lead to wasted
|
|
434
|
+
// CPU time...
|
|
435
|
+
find: async function(func, result='value'){
|
|
436
|
+
var that = this
|
|
437
|
+
// NOTE: not using pure await here as this is simpler to actually
|
|
438
|
+
// control the moment the resulting promise resolves without
|
|
439
|
+
// the need for juggling state...
|
|
440
|
+
return new Promise(function(resolve, reject){
|
|
441
|
+
var resolved = false
|
|
442
|
+
that.map(function(elem){
|
|
443
|
+
var res = func(elem)
|
|
444
|
+
if(res){
|
|
445
|
+
resolved = true
|
|
446
|
+
resolve(
|
|
447
|
+
result == 'bool' ?
|
|
448
|
+
true
|
|
449
|
+
: result == 'result' ?
|
|
450
|
+
res
|
|
451
|
+
: elem)
|
|
452
|
+
// XXX EXPEREMENTAL: STOP...
|
|
453
|
+
// NOTE: we do not need to throw STOP here
|
|
454
|
+
// but it can prevent some overhead...
|
|
455
|
+
if(that.constructor.STOP){
|
|
456
|
+
throw that.constructor.STOP } } })
|
|
457
|
+
.then(function(){
|
|
458
|
+
resolved
|
|
459
|
+
|| resolve(
|
|
460
|
+
result == 'bool' ?
|
|
461
|
+
false
|
|
462
|
+
: undefined) }) }) },
|
|
463
|
+
findIndex: promiseProxy('findIndex'),
|
|
464
|
+
|
|
465
|
+
// NOTE: this is just a special-case of .find(..)
|
|
466
|
+
some: async function(func){
|
|
467
|
+
return this.find(func, 'bool') },
|
|
324
468
|
every: promiseProxy('every'),
|
|
325
|
-
// XXX this can be lazy...
|
|
326
|
-
some: promiseProxy('some'),
|
|
327
469
|
|
|
328
470
|
|
|
471
|
+
// this is defined globally as Promise.prototype.iter(..)
|
|
472
|
+
//
|
|
473
|
+
// for details see: PromiseMixin(..) below...
|
|
474
|
+
//iter: function(handler=undefined){ ... },
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
// promise api...
|
|
478
|
+
//
|
|
329
479
|
// Overload .then(..), .catch(..) and .finally(..) to return a plain
|
|
330
480
|
// Promise instnace...
|
|
331
481
|
//
|
|
@@ -351,6 +501,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
351
501
|
: p },
|
|
352
502
|
|
|
353
503
|
|
|
504
|
+
// constructor...
|
|
354
505
|
//
|
|
355
506
|
// Promise.iter([ .. ])
|
|
356
507
|
// -> iterable-promise
|
|
@@ -370,11 +521,12 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
370
521
|
// items...
|
|
371
522
|
// XXX we can make the index a promise, then if the client needs
|
|
372
523
|
// the value they can wait for it...
|
|
524
|
+
// ...this may be quite an overhead...
|
|
373
525
|
//
|
|
374
526
|
//
|
|
375
527
|
// Special cases useful for extending this constructor...
|
|
376
528
|
//
|
|
377
|
-
// Set raw .
|
|
529
|
+
// Set raw .__packed without any pre-processing...
|
|
378
530
|
// Promise.iter([ .. ], 'raw')
|
|
379
531
|
// -> iterable-promise
|
|
380
532
|
//
|
|
@@ -383,11 +535,9 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
383
535
|
// -> iterable-promise
|
|
384
536
|
//
|
|
385
537
|
//
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// needed...
|
|
390
|
-
// XXX how do we handle errors/rejections???
|
|
538
|
+
// NOTE: if 'types/Array' is imported this will support throwing STOP,
|
|
539
|
+
// for more info see notes for .__pack(..)
|
|
540
|
+
// XXX EXPEREMENTAL: STOP...
|
|
391
541
|
__new__: function(_, list, handler){
|
|
392
542
|
// instance...
|
|
393
543
|
var promise
|
|
@@ -408,9 +558,9 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
408
558
|
// handle/pack input data...
|
|
409
559
|
if(handler != 'raw'){
|
|
410
560
|
list = list instanceof IterablePromise ?
|
|
411
|
-
this.__handle(list.
|
|
561
|
+
this.__handle(list.__packed, handler)
|
|
412
562
|
: this.__pack(list, handler) }
|
|
413
|
-
Object.defineProperty(obj, '
|
|
563
|
+
Object.defineProperty(obj, '__packed', {
|
|
414
564
|
value: list,
|
|
415
565
|
enumerable: false,
|
|
416
566
|
})
|
|
@@ -473,13 +623,14 @@ object.Constructor('InteractivePromise', Promise, {
|
|
|
473
623
|
// register a handler...
|
|
474
624
|
} else {
|
|
475
625
|
var h = obj == null ?
|
|
476
|
-
// NOTE: we need to get the handlers from
|
|
477
|
-
// unless we are not
|
|
478
|
-
// bootstrap
|
|
479
|
-
//
|
|
480
|
-
//
|
|
481
|
-
//
|
|
482
|
-
//
|
|
626
|
+
// NOTE: we need to get the handlers from
|
|
627
|
+
// .__message_handlers unless we are not
|
|
628
|
+
// fully defined yet, then use the bootstrap
|
|
629
|
+
// container (handlers)...
|
|
630
|
+
// ...since we can call onmessage(..) while
|
|
631
|
+
// the promise is still defined there is no
|
|
632
|
+
// way to .send(..) until it returns a promise
|
|
633
|
+
// object, this races here are highly unlikely...
|
|
483
634
|
handlers
|
|
484
635
|
: (obj.__message_handlers =
|
|
485
636
|
obj.__message_handlers ?? [])
|
|
@@ -507,6 +658,7 @@ object.Constructor('InteractivePromise', Promise, {
|
|
|
507
658
|
// Cooperative promise...
|
|
508
659
|
//
|
|
509
660
|
// A promise that can be resolved/rejected externally.
|
|
661
|
+
//
|
|
510
662
|
// NOTE: normally this has no internal resolver logic...
|
|
511
663
|
//
|
|
512
664
|
|
|
@@ -566,10 +718,13 @@ object.Constructor('CooperativePromise', Promise, {
|
|
|
566
718
|
var ProxyPromise =
|
|
567
719
|
module.ProxyPromise =
|
|
568
720
|
object.Constructor('ProxyPromise', Promise, {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
721
|
+
|
|
722
|
+
then: IterablePromise.prototype.then,
|
|
723
|
+
|
|
724
|
+
__new__: function(context, other, nooverride=false){
|
|
725
|
+
var proto = 'prototype' in other ?
|
|
726
|
+
other.prototype
|
|
727
|
+
: other
|
|
573
728
|
var obj = Reflect.construct(
|
|
574
729
|
ProxyPromise.__proto__,
|
|
575
730
|
[function(resolve, reject){
|
|
@@ -580,6 +735,9 @@ object.Constructor('ProxyPromise', Promise, {
|
|
|
580
735
|
// NOTE: we are not using object.deepKeys(..) here as we need
|
|
581
736
|
// the key origin not to trigger property getters...
|
|
582
737
|
var seen = new Set()
|
|
738
|
+
nooverride = nooverride instanceof Array ?
|
|
739
|
+
new Set(nooverride)
|
|
740
|
+
: nooverride
|
|
583
741
|
while(proto != null){
|
|
584
742
|
Object.entries(Object.getOwnPropertyDescriptors(proto))
|
|
585
743
|
.forEach(function([key, value]){
|
|
@@ -594,6 +752,13 @@ object.Constructor('ProxyPromise', Promise, {
|
|
|
594
752
|
&& Object.prototype.run === value.value)
|
|
595
753
|
&& !value.enumerable){
|
|
596
754
|
return }
|
|
755
|
+
// do not override existing methods...
|
|
756
|
+
if(nooverride === true ?
|
|
757
|
+
key in obj
|
|
758
|
+
: nooverride instanceof Set ?
|
|
759
|
+
nooverride.has(key)
|
|
760
|
+
: nooverride){
|
|
761
|
+
return }
|
|
597
762
|
// proxy...
|
|
598
763
|
obj[key] = promiseProxy(key) })
|
|
599
764
|
proto = proto.__proto__ }
|
|
@@ -619,7 +784,7 @@ var PromiseProtoMixin =
|
|
|
619
784
|
module.PromiseProtoMixin =
|
|
620
785
|
object.Mixin('PromiseProtoMixin', 'soft', {
|
|
621
786
|
as: ProxyPromise,
|
|
622
|
-
iter: function(handler){
|
|
787
|
+
iter: function(handler=undefined){
|
|
623
788
|
return IterablePromise(this, handler) },
|
|
624
789
|
})
|
|
625
790
|
|
|
@@ -629,4 +794,4 @@ PromiseProtoMixin(Promise.prototype)
|
|
|
629
794
|
|
|
630
795
|
|
|
631
796
|
/**********************************************************************
|
|
632
|
-
* vim:set ts=4 sw=4 :
|
|
797
|
+
* vim:set ts=4 sw=4 nowrap : */ return module })
|
package/README.md
CHANGED
|
@@ -77,8 +77,14 @@ Library of JavaScript type extensions, types and utilities.
|
|
|
77
77
|
- [`<promise-iter>.reverse()`](#promise-iterreverse)
|
|
78
78
|
- [`<promise-iter>.concat(..)`](#promise-iterconcat)
|
|
79
79
|
- [`<promise-iter>.push(..)` / `<promise-iter>.unshift(..)`](#promise-iterpush--promise-iterunshift)
|
|
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)
|
|
82
|
+
- [Array proxy methods returning `<promise-iter>`](#array-proxy-methods-returning-promise-iter)
|
|
83
|
+
- [Array proxy methods returning a `<promise>`](#array-proxy-methods-returning-a-promise)
|
|
80
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)
|
|
81
86
|
- [Advanced handler](#advanced-handler)
|
|
87
|
+
- [Stopping the iteration](#stopping-the-iteration)
|
|
82
88
|
- [Promise proxies](#promise-proxies)
|
|
83
89
|
- [`<promise>.as(..)`](#promiseas)
|
|
84
90
|
- [`<promise-proxy>.<method>(..)`](#promise-proxymethod)
|
|
@@ -1663,6 +1669,95 @@ and [`<array>.unshift(..)`](https://developer.mozilla.org/en-US/docs/Web/JavaScr
|
|
|
1663
1669
|
see them for more info.
|
|
1664
1670
|
|
|
1665
1671
|
|
|
1672
|
+
#### `<promise-iter>.at(..)` / `<promise-iter>.first()` / `<promise-iter>.last()`
|
|
1673
|
+
|
|
1674
|
+
Proxies to the appropriate array methods with a special-case: when getting elements
|
|
1675
|
+
at positions `0` or `-1` (i.e. `.first()` / `.last()`) these _can_ resolve before the
|
|
1676
|
+
parent `<promise-iter>`.
|
|
1677
|
+
|
|
1678
|
+
XXX
|
|
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.
|
|
1720
|
+
|
|
1721
|
+
#### Array proxy methods returning `<promise-iter>`
|
|
1722
|
+
|
|
1723
|
+
- `<promise-iter>.sort(..)`
|
|
1724
|
+
- `<promise-iter>.slice(..)`
|
|
1725
|
+
- `<promise-iter>.entries()` / `<promise-iter>.keys()` / `<promise-iter>.values()`
|
|
1726
|
+
|
|
1727
|
+
These methods are proxies to the appropriate array methods.
|
|
1728
|
+
|
|
1729
|
+
```bnf
|
|
1730
|
+
<promise-iter>.<method>(..)
|
|
1731
|
+
-> <promise-iter>
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
These methods need the parent `<promise-iter>` to resolve before resolving themselves.
|
|
1735
|
+
|
|
1736
|
+
XXX links...
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
#### Array proxy methods returning a `<promise>`
|
|
1740
|
+
|
|
1741
|
+
- `<promise-iter>.indexOf(..)`
|
|
1742
|
+
- `<promise-iter>.includes(..)`
|
|
1743
|
+
- `<promise-iter>.every(..)`
|
|
1744
|
+
- `<promise-iter>.findIndex(..)`
|
|
1745
|
+
|
|
1746
|
+
These methods are proxies to the appropriate array methods.
|
|
1747
|
+
|
|
1748
|
+
```bnf
|
|
1749
|
+
<promise-iter>.<method>(..)
|
|
1750
|
+
-> <promise>
|
|
1751
|
+
```
|
|
1752
|
+
|
|
1753
|
+
These methods need the parent `<promise-iter>` to resolve before resolving themselves.
|
|
1754
|
+
|
|
1755
|
+
Since the equivalent array methods do not return iterables these will return a basic
|
|
1756
|
+
(non-iterable) `<promise>`.
|
|
1757
|
+
|
|
1758
|
+
XXX links...
|
|
1759
|
+
|
|
1760
|
+
|
|
1666
1761
|
#### `<promise-iter>.then(..)` / `<promise-iter>.catch(..)` / `<promise-iter>.finally(..)`
|
|
1667
1762
|
|
|
1668
1763
|
An extension to
|
|
@@ -1676,6 +1771,16 @@ this adds the ability to pass no arguments
|
|
|
1676
1771
|
This will return a generic promise wrapper passing through the results as-is. This
|
|
1677
1772
|
can be useful to hide the extended promise API from further code.
|
|
1678
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
|
+
|
|
1679
1784
|
|
|
1680
1785
|
#### Advanced handler
|
|
1681
1786
|
|
|
@@ -1727,6 +1832,24 @@ var p = Promise.iter(
|
|
|
1727
1832
|
console.log(lst) }) // -> [2, 2, 4, 4, [5, 6]]
|
|
1728
1833
|
```
|
|
1729
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
|
+
|
|
1730
1853
|
|
|
1731
1854
|
### Promise proxies
|
|
1732
1855
|
|
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,
|
|
@@ -428,6 +436,41 @@ ITERATOR_PROTOTYPES
|
|
|
428
436
|
GeneratorProtoMixin(proto) })
|
|
429
437
|
|
|
430
438
|
|
|
439
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
440
|
+
// XXX EXPERIMENTAL...
|
|
441
|
+
|
|
442
|
+
var makeAsyncGenerator = function(name, pre){
|
|
443
|
+
return function(...args){
|
|
444
|
+
var that = this
|
|
445
|
+
return Object.assign(
|
|
446
|
+
async function*(){
|
|
447
|
+
var a = pre ?
|
|
448
|
+
pre.call(this, args, ...arguments)
|
|
449
|
+
: args
|
|
450
|
+
for await (var e of that(...arguments)[name](...a)){
|
|
451
|
+
yield e } },
|
|
452
|
+
{ toString: function(){
|
|
453
|
+
return [
|
|
454
|
+
that.toString(),
|
|
455
|
+
// XXX need to normalize args better...
|
|
456
|
+
`.${ name }(${ args.join(', ') })`,
|
|
457
|
+
].join('\n ') }, }) } }
|
|
458
|
+
|
|
459
|
+
var AsyncGeneratorMixin =
|
|
460
|
+
module.AsyncGeneratorMixin =
|
|
461
|
+
object.Mixin('AsyncGeneratorMixin', 'soft', {
|
|
462
|
+
map: makeAsyncGenerator('map'),
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
var AsyncGeneratorProtoMixin =
|
|
466
|
+
module.AsyncGeneratorProtoMixin =
|
|
467
|
+
object.Mixin('AsyncGeneratorProtoMixin', 'soft', {
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
//AsyncGeneratorMixin(AsyncGeneratorPrototype)
|
|
471
|
+
//AsyncGeneratorProtoMixin(AsyncGeneratorPrototype.prototype)
|
|
472
|
+
|
|
473
|
+
|
|
431
474
|
|
|
432
475
|
//---------------------------------------------------------------------
|
|
433
476
|
// Generators...
|