ig-types 6.24.15 → 6.24.18
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/Promise.js +301 -289
- package/package.json +1 -1
- package/test.js +171 -42
package/Promise.js
CHANGED
|
@@ -34,6 +34,253 @@ var generator = require('./generator')
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
//---------------------------------------------------------------------
|
|
38
|
+
// XXX EXPERIMENTING...
|
|
39
|
+
|
|
40
|
+
// packed format API...
|
|
41
|
+
//
|
|
42
|
+
// XXX should this be a container or just a set of function???
|
|
43
|
+
// ...for simplicity I'll keep it a stateless set of functions for
|
|
44
|
+
// now, to avoid yet another layer of indirection -- IterablePromise...
|
|
45
|
+
var packed =
|
|
46
|
+
module.packed =
|
|
47
|
+
{
|
|
48
|
+
//
|
|
49
|
+
// pack(<array>)
|
|
50
|
+
// pack(<promise>)
|
|
51
|
+
// -> <packed>
|
|
52
|
+
//
|
|
53
|
+
// <packed> ::=
|
|
54
|
+
// <packed-array>
|
|
55
|
+
// | <packed-promise>
|
|
56
|
+
//
|
|
57
|
+
// <packed-array> ::=
|
|
58
|
+
// [
|
|
59
|
+
// <item>
|
|
60
|
+
// | <items> - array
|
|
61
|
+
// | <promise-item>
|
|
62
|
+
// | <promise-items>,
|
|
63
|
+
// ...
|
|
64
|
+
// ]
|
|
65
|
+
//
|
|
66
|
+
// NOTE: when all promises are expanded the packed array can be unpacked
|
|
67
|
+
// by simply calling .flat()
|
|
68
|
+
// NOTE: if 'types/Array' is imported this will support throwing STOP
|
|
69
|
+
// from the handler.
|
|
70
|
+
// Due to the async nature of promises though the way stops are
|
|
71
|
+
// handled may be unpredictable -- the handlers can be run out
|
|
72
|
+
// of order, as the nested promises resolve and thus throwing
|
|
73
|
+
// stop will stop the handlers not yet run and not the next
|
|
74
|
+
// handlers in sequence.
|
|
75
|
+
//
|
|
76
|
+
// XXX see if we should self-expand promises...
|
|
77
|
+
// XXX migrate these back into InteractivePromise...
|
|
78
|
+
// XXX does this need onerror(..) ???
|
|
79
|
+
pack: function(list){
|
|
80
|
+
var that = this
|
|
81
|
+
// list: promise...
|
|
82
|
+
// XXX should we just test for typeof(list.then) == 'function'???
|
|
83
|
+
if(list instanceof Promise
|
|
84
|
+
// list: async promise...
|
|
85
|
+
|| (typeof(list) == 'object'
|
|
86
|
+
&& list.then
|
|
87
|
+
&& Symbol.asyncIterator in list)){
|
|
88
|
+
return list
|
|
89
|
+
.then(this.pack.bind(this)) }
|
|
90
|
+
// list: generator...
|
|
91
|
+
if(typeof(list) == 'object'
|
|
92
|
+
&& !list.map
|
|
93
|
+
&& Symbol.iterator in list){
|
|
94
|
+
list = [...list] }
|
|
95
|
+
// list: non-array / non-iterable...
|
|
96
|
+
if(typeof(list.map) != 'function'){
|
|
97
|
+
list = [list].flat() }
|
|
98
|
+
// list: array...
|
|
99
|
+
// XXX should we self-expand this???
|
|
100
|
+
return list
|
|
101
|
+
.map(function(elem){
|
|
102
|
+
// XXX should we expand generators here???
|
|
103
|
+
return elem instanceof Array ?
|
|
104
|
+
[elem]
|
|
105
|
+
// XXX should we just test for .then(..)???
|
|
106
|
+
// XXX should we self-expand this???
|
|
107
|
+
: elem instanceof Promise ?
|
|
108
|
+
elem.then(function(elem){
|
|
109
|
+
return elem instanceof Array ?
|
|
110
|
+
[elem]
|
|
111
|
+
: elem })
|
|
112
|
+
: elem }) },
|
|
113
|
+
//
|
|
114
|
+
// handle(<packed>, <handler>[, <onerror>])
|
|
115
|
+
// -> <packed>
|
|
116
|
+
//
|
|
117
|
+
// XXX revise nested promise handling...
|
|
118
|
+
// ...something like simplify(<packed>) -> <packed> ???
|
|
119
|
+
// XXX STOP_PROMISED_HANDLERS -- TEST
|
|
120
|
+
handle: function(packed, handler, onerror){
|
|
121
|
+
var that = this
|
|
122
|
+
var handlers = [...arguments].slice(1)
|
|
123
|
+
|
|
124
|
+
if(packed instanceof Promise){
|
|
125
|
+
return packed.then(function(packed){
|
|
126
|
+
return that.handle(packed, ...handlers) }) }
|
|
127
|
+
|
|
128
|
+
var handleSTOP = function(err){
|
|
129
|
+
if(err && err === Array.STOP){
|
|
130
|
+
stop = true
|
|
131
|
+
return []
|
|
132
|
+
} else if(err && err instanceof Array.STOP){
|
|
133
|
+
return err.value }
|
|
134
|
+
throw err }
|
|
135
|
+
|
|
136
|
+
var stop = false
|
|
137
|
+
var map = Array.STOP ?
|
|
138
|
+
'smap'
|
|
139
|
+
: 'map'
|
|
140
|
+
return packed
|
|
141
|
+
// NOTE: we do not need to rapack after this because the handlers
|
|
142
|
+
// will get the correct (unpacked) values and it's their
|
|
143
|
+
// responsibility to pack them if needed...
|
|
144
|
+
// NOTE: this removes the need to handle sub-arrays unless they are
|
|
145
|
+
// in a promise...
|
|
146
|
+
.flat()
|
|
147
|
+
[map](
|
|
148
|
+
function(elem){
|
|
149
|
+
return elem instanceof Promise ?
|
|
150
|
+
elem.then(function(elem){
|
|
151
|
+
if(stop){
|
|
152
|
+
return [] }
|
|
153
|
+
try{
|
|
154
|
+
var has_promise = false
|
|
155
|
+
// NOTE: do the same thing handle(..) does
|
|
156
|
+
// but on a single level, without expanding
|
|
157
|
+
// arrays...
|
|
158
|
+
if(elem instanceof Array){
|
|
159
|
+
var res = elem.map(function(elem){
|
|
160
|
+
var res = elem instanceof Promise ?
|
|
161
|
+
elem.then(function(elem){
|
|
162
|
+
try{
|
|
163
|
+
return !stop ?
|
|
164
|
+
handler(elem)
|
|
165
|
+
: []
|
|
166
|
+
} catch(err){
|
|
167
|
+
return handleSTOP(err) } })
|
|
168
|
+
: handler(elem)
|
|
169
|
+
has_promise = has_promise
|
|
170
|
+
|| res instanceof Promise
|
|
171
|
+
return res })
|
|
172
|
+
// non-arrays...
|
|
173
|
+
} else {
|
|
174
|
+
// NOTE: we are wrapping the result in an array to
|
|
175
|
+
// normalize it with the above...
|
|
176
|
+
res = [handler(elem)]
|
|
177
|
+
has_promise = has_promise
|
|
178
|
+
|| res[0] instanceof Promise }
|
|
179
|
+
|
|
180
|
+
// compensate for the outer .flat()...
|
|
181
|
+
// NOTE: at this point res is always an array...
|
|
182
|
+
return has_promise ?
|
|
183
|
+
// NOTE: since we are already in a promise
|
|
184
|
+
// grouping things here is not a big
|
|
185
|
+
// deal, however this is needed to link
|
|
186
|
+
// nested promises with the containing
|
|
187
|
+
// promise...
|
|
188
|
+
Promise.all(res)
|
|
189
|
+
.then(function(res){
|
|
190
|
+
return res.flat() })
|
|
191
|
+
: res.flat()
|
|
192
|
+
} catch(err){
|
|
193
|
+
return handleSTOP(err) } })
|
|
194
|
+
: handler(elem) },
|
|
195
|
+
// onerror...
|
|
196
|
+
function(err){
|
|
197
|
+
stop = true
|
|
198
|
+
typeof(onerror) == 'function'
|
|
199
|
+
&& onerror(err) }) },
|
|
200
|
+
//
|
|
201
|
+
// unpack(<packed>)
|
|
202
|
+
// -> <array>
|
|
203
|
+
//
|
|
204
|
+
unpack: function(packed){
|
|
205
|
+
if(packed instanceof Promise){
|
|
206
|
+
return packed.then(this.unpack.bind(this)) }
|
|
207
|
+
var input = packed
|
|
208
|
+
// NOTE: we expand promises on the first two levels of the packed
|
|
209
|
+
// list and then flatten the result...
|
|
210
|
+
for(var [i, elem] of Object.entries(packed)){
|
|
211
|
+
// expand promises in lists...
|
|
212
|
+
if(elem instanceof Array){
|
|
213
|
+
for(var e of elem){
|
|
214
|
+
if(e instanceof Promise){
|
|
215
|
+
// copy the input (on demand) list as we'll need to
|
|
216
|
+
// modify it...
|
|
217
|
+
packed === input
|
|
218
|
+
&& (packed = packed.slice())
|
|
219
|
+
// NOTE: this will immediately be caught by the
|
|
220
|
+
// Promise condition below...
|
|
221
|
+
elem = packed[i] =
|
|
222
|
+
Promise.all(elem)
|
|
223
|
+
break } } }
|
|
224
|
+
// expand promises...
|
|
225
|
+
if(elem instanceof Promise){
|
|
226
|
+
return Promise.all(packed)
|
|
227
|
+
.then(this.unpack.bind(this)) } }
|
|
228
|
+
// the list is expanded here...
|
|
229
|
+
return packed.flat() },
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
//
|
|
234
|
+
// itemResolved(<list>, <onupdate>[, <ondone>])
|
|
235
|
+
// itemResolved(<list>, <options>)
|
|
236
|
+
// -> <list>
|
|
237
|
+
//
|
|
238
|
+
// <onupdate>(<elem>, <index>, <list>)
|
|
239
|
+
//
|
|
240
|
+
// <ondone>(<list>)
|
|
241
|
+
//
|
|
242
|
+
// options format:
|
|
243
|
+
// {
|
|
244
|
+
// onupdate: <func>,
|
|
245
|
+
//
|
|
246
|
+
// // optional
|
|
247
|
+
// ondone: <func>,
|
|
248
|
+
// }
|
|
249
|
+
//
|
|
250
|
+
// XXX rename to Promise.each(..) or Promise.eachPromise(..) ???
|
|
251
|
+
var itemResolved =
|
|
252
|
+
module.itemResolved =
|
|
253
|
+
function(list, onupdate, ondone){
|
|
254
|
+
if(typeof(onupdate) != 'function'){
|
|
255
|
+
var {onupdate, ondone} = onupdate }
|
|
256
|
+
typeof(ondone) == 'function'
|
|
257
|
+
&& Promise.all(list)
|
|
258
|
+
.then(ondone)
|
|
259
|
+
for(let [i, elem] of Object.entries(list)){
|
|
260
|
+
if(elem instanceof Promise){
|
|
261
|
+
elem.then(function(elem){
|
|
262
|
+
onupdate(elem, i, list) }) } }
|
|
263
|
+
return list }
|
|
264
|
+
|
|
265
|
+
// Like itemResolved(..) but both onupdate and ondone are optional...
|
|
266
|
+
//
|
|
267
|
+
// XXX do not modify the array after this is called and until ondone(..) is called...
|
|
268
|
+
// XXX should this know how to expand things???
|
|
269
|
+
var selfResolve =
|
|
270
|
+
module.selfResolve =
|
|
271
|
+
function(list, onupdate, ondone){
|
|
272
|
+
if(onupdate
|
|
273
|
+
&& typeof(onupdate) != 'function'){
|
|
274
|
+
var {onupdate, ondone} = onupdate }
|
|
275
|
+
return itemResolved(list,
|
|
276
|
+
function(elem, i, list){
|
|
277
|
+
list[i] = elem
|
|
278
|
+
typeof(onupdate) == 'function'
|
|
279
|
+
&& onupdate(elem, i, list) },
|
|
280
|
+
ondone) }
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
37
284
|
//---------------------------------------------------------------------
|
|
38
285
|
// Iterable promise...
|
|
39
286
|
//
|
|
@@ -63,6 +310,9 @@ var generator = require('./generator')
|
|
|
63
310
|
// XXX add support for async generators...
|
|
64
311
|
//
|
|
65
312
|
|
|
313
|
+
var EMPTY = {doc: 'empty placeholder'}
|
|
314
|
+
|
|
315
|
+
|
|
66
316
|
var iterPromiseProxy =
|
|
67
317
|
module.iterPromiseProxy =
|
|
68
318
|
function(name){
|
|
@@ -114,284 +364,45 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
114
364
|
// NOTE: these can be useful for debugging and extending...
|
|
115
365
|
//
|
|
116
366
|
// pack and oprionally transform/handle an array (sync)...
|
|
117
|
-
//
|
|
118
|
-
// NOTE: if 'types/Array' is imported this will support throwing STOP
|
|
119
|
-
// from the handler.
|
|
120
|
-
// Due to the async nature of promises though the way stops are
|
|
121
|
-
// handled may be unpredictable -- the handlers can be run out
|
|
122
|
-
// of order, as the nested promises resolve and thus throwing
|
|
123
|
-
// stop will stop the handlers not yet run and not the next
|
|
124
|
-
// handlers in sequence.
|
|
125
|
-
// XXX EXPEREMENTAL: STOP...
|
|
126
|
-
// XXX ITER can we unwind (sync/async) generators one by one???
|
|
127
|
-
/* XXX this repeats part of the functionality of .__handle(..)
|
|
128
367
|
__pack: function(list, handler=undefined, onerror=undefined){
|
|
129
|
-
var that = this
|
|
130
|
-
// handle iterator...
|
|
131
|
-
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
132
|
-
if(typeof(list) == 'object'
|
|
133
|
-
&& Symbol.iterator in list){
|
|
134
|
-
if(!onerror){
|
|
135
|
-
list = [...list]
|
|
136
|
-
// handle errors in input generator...
|
|
137
|
-
// NOTE: this does not offer the most control because semantically
|
|
138
|
-
// we bust behave in the same manner as <generator>.iter(..)
|
|
139
|
-
} else {
|
|
140
|
-
var res = []
|
|
141
|
-
try{
|
|
142
|
-
for(var e of list){
|
|
143
|
-
res.push(e) }
|
|
144
|
-
}catch(err){
|
|
145
|
-
var r = onerror(err)
|
|
146
|
-
r !== undefined
|
|
147
|
-
&& res.push(r) }
|
|
148
|
-
list = res } }
|
|
149
368
|
// handle iterable promise...
|
|
150
369
|
if(list instanceof IterablePromise){
|
|
151
370
|
return this.__handle(list.__packed, handler, onerror) }
|
|
152
371
|
// handle promise / async-iterator...
|
|
153
|
-
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
154
372
|
if(typeof(list) == 'object'
|
|
155
373
|
&& Symbol.asyncIterator in list){
|
|
156
374
|
return list
|
|
157
375
|
.iter(handler, onerror)
|
|
158
376
|
.then(function(list){
|
|
159
377
|
return that.__pack(list) }) }
|
|
160
|
-
if(list instanceof Promise){
|
|
161
|
-
return list
|
|
162
|
-
.then(function(list){
|
|
163
|
-
return that.__pack(list, handler, onerror) }) }
|
|
164
|
-
// do the work...
|
|
165
|
-
// NOTE: packing and handling are mixed here because it's faster
|
|
166
|
-
// to do them both on a single list traverse...
|
|
167
|
-
var handle = !!handler
|
|
168
|
-
handler = handler
|
|
169
|
-
?? function(elem){
|
|
170
|
-
return [elem] }
|
|
171
|
-
// XXX this repeats .__handle(..), need to unify...
|
|
172
|
-
var stop = false
|
|
173
|
-
var map = 'map'
|
|
174
|
-
var pack = function(){
|
|
175
|
-
return [list].flat()
|
|
176
|
-
[map](function(elem){
|
|
177
|
-
// XXX EXPEREMENTAL...
|
|
178
|
-
return elem instanceof IterablePromise ?
|
|
179
|
-
(elem.isSync() ?
|
|
180
|
-
handler(elem.sync())
|
|
181
|
-
// XXX need to handle this but keep it IterablePromise...
|
|
182
|
-
: elem.iterthen(handler))
|
|
183
|
-
: (elem instanceof SyncPromise
|
|
184
|
-
&& !(elem.sync() instanceof Promise)) ?
|
|
185
|
-
handler(elem.sync())
|
|
186
|
-
: elem && elem.then ?
|
|
187
|
-
(handleSTOP ?
|
|
188
|
-
// stoppable -- need to handle stop async...
|
|
189
|
-
elem
|
|
190
|
-
.then(function(res){
|
|
191
|
-
return !stop ?
|
|
192
|
-
handler(res)
|
|
193
|
-
: [] })
|
|
194
|
-
// NOTE: we are using .catch(..) here
|
|
195
|
-
// instead of directly passing the
|
|
196
|
-
// error handler to be able to catch
|
|
197
|
-
// the STOP from the handler...
|
|
198
|
-
.catch(handleSTOP)
|
|
199
|
-
// non-stoppable...
|
|
200
|
-
: elem.then(handler))
|
|
201
|
-
: elem instanceof Array ?
|
|
202
|
-
handler(elem)
|
|
203
|
-
// NOTE: we keep things that do not need protecting
|
|
204
|
-
// from .flat() as-is...
|
|
205
|
-
: !handle ?
|
|
206
|
-
elem
|
|
207
|
-
: handler(elem) }) }
|
|
208
|
-
// pack (stoppable)...
|
|
209
|
-
if(!!this.constructor.STOP){
|
|
210
|
-
map = 'smap'
|
|
211
|
-
var handleSTOP = function(err){
|
|
212
|
-
// handle stop...
|
|
213
|
-
stop = err
|
|
214
|
-
if(err === that.constructor.STOP
|
|
215
|
-
|| err instanceof that.constructor.STOP){
|
|
216
|
-
return 'value' in err ?
|
|
217
|
-
err.value
|
|
218
|
-
: [] }
|
|
219
|
-
throw err }
|
|
220
|
-
try{
|
|
221
|
-
return pack()
|
|
222
|
-
}catch(err){
|
|
223
|
-
return handleSTOP(err) } }
|
|
224
378
|
|
|
225
|
-
//
|
|
226
|
-
|
|
227
|
-
// transform/handle packed array (sync, but can return promises in the list)...
|
|
228
|
-
__handle: function(list, handler=undefined, onerror=undefined){
|
|
229
|
-
var that = this
|
|
230
|
-
if(typeof(list) == 'function'){
|
|
231
|
-
handler = list
|
|
232
|
-
list = this.__packed }
|
|
233
|
-
if(!handler){
|
|
234
|
-
return list }
|
|
235
|
-
// handle promise list...
|
|
236
|
-
if(list instanceof Promise){
|
|
237
|
-
return list.then(function(list){
|
|
238
|
-
return that.__handle(list, handler, onerror) }) }
|
|
239
|
-
// do the work...
|
|
240
|
-
// NOTE: since each section of the packed .__array is the same
|
|
241
|
-
// structure as the input we'll use .__pack(..) to handle
|
|
242
|
-
// them, this also keeps all the handling code in one place.
|
|
243
|
-
var map = !!this.constructor.STOP ?
|
|
244
|
-
'smap'
|
|
245
|
-
: 'map'
|
|
246
|
-
return list[map](function(elem){
|
|
247
|
-
elem = elem instanceof Array
|
|
248
|
-
|| elem instanceof Promise ?
|
|
249
|
-
that.__pack(elem, handler, onerror)
|
|
250
|
-
: [handler(elem)]
|
|
251
|
-
elem = elem instanceof Promise ?
|
|
252
|
-
elem.then(function([e]){
|
|
253
|
-
return e })
|
|
254
|
-
: elem
|
|
255
|
-
return elem })
|
|
256
|
-
.flat() },
|
|
257
|
-
/*/
|
|
258
|
-
__pack: function(list, handler=undefined, onerror=undefined){
|
|
259
|
-
var that = this
|
|
260
|
-
// handle iterator...
|
|
261
|
-
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
262
|
-
if(typeof(list) == 'object'
|
|
263
|
-
&& Symbol.iterator in list){
|
|
264
|
-
if(!onerror){
|
|
265
|
-
list = [...list]
|
|
266
|
-
// handle errors in input generator...
|
|
267
|
-
// NOTE: this does not offer the most control because semantically
|
|
268
|
-
// we bust behave in the same manner as <generator>.iter(..)
|
|
269
|
-
} else {
|
|
270
|
-
var res = []
|
|
271
|
-
try{
|
|
272
|
-
for(var e of list){
|
|
273
|
-
res.push(e) }
|
|
274
|
-
}catch(err){
|
|
275
|
-
var r = onerror(err)
|
|
276
|
-
r !== undefined
|
|
277
|
-
&& res.push(r) }
|
|
278
|
-
list = res } }
|
|
279
|
-
// handle iterable promise...
|
|
280
|
-
if(list instanceof IterablePromise){
|
|
281
|
-
return this.__handle(list.__packed, handler, onerror) }
|
|
282
|
-
// handle promise / async-iterator...
|
|
283
|
-
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
284
|
-
if(typeof(list) == 'object'
|
|
285
|
-
&& Symbol.asyncIterator in list){
|
|
286
|
-
return list
|
|
287
|
-
.iter(handler, onerror)
|
|
288
|
-
.then(function(list){
|
|
289
|
-
return that.__pack(list) }) }
|
|
290
|
-
if(list instanceof Promise){
|
|
291
|
-
return list
|
|
292
|
-
.then(function(list){
|
|
293
|
-
return that.__pack(list, handler, onerror) }) }
|
|
294
|
-
// pack...
|
|
295
|
-
list = [list].flat()
|
|
296
|
-
.map(function(elem){
|
|
297
|
-
return elem instanceof Array ?
|
|
298
|
-
[elem]
|
|
299
|
-
: elem instanceof Promise ?
|
|
300
|
-
elem.then(function(e){
|
|
301
|
-
return [e] })
|
|
302
|
-
: elem })
|
|
379
|
+
// do the packing...
|
|
380
|
+
var packed = module.packed.pack(list)
|
|
303
381
|
// handle if needed...
|
|
304
382
|
return handler ?
|
|
305
|
-
this.__handle(
|
|
306
|
-
:
|
|
383
|
+
this.__handle(packed, handler, onerror)
|
|
384
|
+
: packed },
|
|
307
385
|
// transform/handle packed array (sync, but can return promises in the list)...
|
|
308
|
-
|
|
309
|
-
__handle: function(list, handler=undefined, onerror=undefined){
|
|
386
|
+
__handle: function(packed, handler=undefined, onerror=undefined){
|
|
310
387
|
var that = this
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
388
|
+
// do .__handle(<func>)
|
|
389
|
+
if(typeof(packed) == 'function'){
|
|
390
|
+
handler = packed
|
|
391
|
+
packed = this.__packed }
|
|
314
392
|
if(!handler){
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
if(list instanceof Promise){
|
|
318
|
-
return list.then(function(list){
|
|
319
|
-
return that.__handle(list, handler, onerror) }) }
|
|
320
|
-
// do the work...
|
|
321
|
-
list = list instanceof Array ?
|
|
322
|
-
list
|
|
323
|
-
: [list]
|
|
324
|
-
var map = !!this.constructor.STOP ?
|
|
325
|
-
'smap'
|
|
326
|
-
: 'map'
|
|
327
|
-
var stop = false
|
|
328
|
-
// XXX do we handle generators here???
|
|
329
|
-
var each = function(elem){
|
|
330
|
-
return elem instanceof Array ?
|
|
331
|
-
elem
|
|
332
|
-
.map(handler)
|
|
333
|
-
.flat()
|
|
334
|
-
: handler(elem) }
|
|
335
|
-
return list
|
|
336
|
-
[map](
|
|
337
|
-
function(elem){
|
|
338
|
-
// NOTE: we are calling .flat() on the result so we
|
|
339
|
-
// need to keep a handled array as a single
|
|
340
|
-
// element by wrapping the return of handled(..)...
|
|
341
|
-
return elem instanceof IterablePromise ?
|
|
342
|
-
(elem.isSync() ?
|
|
343
|
-
each(elem.sync())
|
|
344
|
-
: elem.iterthen(each))
|
|
345
|
-
// sync sync promise...
|
|
346
|
-
: (elem instanceof SyncPromise
|
|
347
|
-
&& !(elem.sync() instanceof Promise)) ?
|
|
348
|
-
[each(elem.sync())]
|
|
349
|
-
// promise / promise-like...
|
|
350
|
-
: elem && elem.then ?
|
|
351
|
-
// NOTE: when this is explicitly stopped we
|
|
352
|
-
// do not call any more handlers after
|
|
353
|
-
// STOP is thrown/returned...
|
|
354
|
-
// NOTE: the promise protects this from .flat()
|
|
355
|
-
elem.then(function(elem){
|
|
356
|
-
return !stop ?
|
|
357
|
-
each(elem)
|
|
358
|
-
: [] })
|
|
359
|
-
: elem instanceof Array ?
|
|
360
|
-
[each(elem)]
|
|
361
|
-
: each(elem) },
|
|
362
|
-
// handle STOP...
|
|
363
|
-
function(){
|
|
364
|
-
stop = true })
|
|
365
|
-
.flat() },
|
|
366
|
-
//*/
|
|
367
|
-
// XXX this should return IterablePromise if .__packed is partially sync (???)
|
|
393
|
+
return packed }
|
|
394
|
+
return module.packed.handle(packed, ...[...arguments].slice(1)) },
|
|
368
395
|
// unpack array (sync/async)...
|
|
369
|
-
__unpack: function(
|
|
370
|
-
|
|
396
|
+
__unpack: function(packed){
|
|
397
|
+
// do .__unpack()
|
|
398
|
+
packed = packed
|
|
371
399
|
?? this.__packed
|
|
372
400
|
// handle promise list...
|
|
373
|
-
if(
|
|
374
|
-
return
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
var res = []
|
|
379
|
-
for(var e of list){
|
|
380
|
-
if(e instanceof IterablePromise){
|
|
381
|
-
e = e.__unpack() }
|
|
382
|
-
if(e instanceof SyncPromise){
|
|
383
|
-
e = e.sync() }
|
|
384
|
-
// give up on a sync solution...
|
|
385
|
-
if(e instanceof Promise){
|
|
386
|
-
// XXX can we return an IterablePromise???
|
|
387
|
-
// XXX is there a more elegant way to do this???
|
|
388
|
-
return Promise.all(list)
|
|
389
|
-
.then(function(list){
|
|
390
|
-
return list.flat() })
|
|
391
|
-
.iter() }
|
|
392
|
-
res.push(e) }
|
|
393
|
-
return res.flat() },
|
|
394
|
-
|
|
401
|
+
if(packed instanceof IterablePromise){
|
|
402
|
+
return packed.__unpack() }
|
|
403
|
+
return module.packed.unpack(packed) },
|
|
404
|
+
|
|
405
|
+
|
|
395
406
|
[Symbol.asyncIterator]: async function*(){
|
|
396
407
|
var list = this.__packed
|
|
397
408
|
if(list instanceof Promise){
|
|
@@ -415,22 +426,19 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
415
426
|
map: function(func){
|
|
416
427
|
return this.constructor(this,
|
|
417
428
|
function(e){
|
|
418
|
-
|
|
419
|
-
return res instanceof Promise ?
|
|
420
|
-
res.then(function(e){
|
|
421
|
-
return [e] })
|
|
422
|
-
: [res] }) },
|
|
429
|
+
return [func(e)] }) },
|
|
423
430
|
filter: function(func){
|
|
424
431
|
return this.constructor(this,
|
|
425
432
|
function(e){
|
|
426
433
|
var res = func(e)
|
|
427
|
-
var _filter = function(elem){
|
|
428
|
-
return res ?
|
|
429
|
-
[elem]
|
|
430
|
-
: [] }
|
|
431
434
|
return res instanceof Promise ?
|
|
432
|
-
res.then(
|
|
433
|
-
|
|
435
|
+
res.then(function(res){
|
|
436
|
+
return res ?
|
|
437
|
+
[e]
|
|
438
|
+
: [] })
|
|
439
|
+
: res ?
|
|
440
|
+
[e]
|
|
441
|
+
: [] }) },
|
|
434
442
|
// NOTE: this does not return an iterable promise as we can't know
|
|
435
443
|
// what the user reduces to...
|
|
436
444
|
// NOTE: the items can be handled out of order because the nested
|
|
@@ -756,6 +764,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
756
764
|
// NOTE: if 'types/Array' is imported this will support throwing STOP,
|
|
757
765
|
// for more info see notes for .__pack(..)
|
|
758
766
|
// XXX EXPEREMENTAL: STOP...
|
|
767
|
+
// XXX use selfResolve(..)
|
|
759
768
|
__new__: function(_, list, handler=undefined, onerror=undefined){
|
|
760
769
|
// instance...
|
|
761
770
|
var promise
|
|
@@ -803,12 +812,16 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
803
812
|
list instanceof Promise ?
|
|
804
813
|
list.then(function(list){
|
|
805
814
|
obj.__packed = list })
|
|
815
|
+
/*/ XXX use selfResolve(..)
|
|
816
|
+
: selfResolve(list) }
|
|
817
|
+
/*/
|
|
806
818
|
: list.forEach(function(elem, i){
|
|
807
819
|
elem instanceof Promise
|
|
808
820
|
&& elem.then(function(elem){
|
|
809
821
|
lst = obj.__packed.slice()
|
|
810
822
|
lst[i] = elem
|
|
811
823
|
obj.__packed = lst }) }) }
|
|
824
|
+
//*/
|
|
812
825
|
return obj },
|
|
813
826
|
})
|
|
814
827
|
|
|
@@ -816,6 +829,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
816
829
|
|
|
817
830
|
//---------------------------------------------------------------------
|
|
818
831
|
// XXX EXPEREMENTAL/HACK...
|
|
832
|
+
// Sequential iterable promise...
|
|
819
833
|
|
|
820
834
|
// This like IterablePromise but guarantees handler execution in order
|
|
821
835
|
// element occurrence.
|
|
@@ -827,7 +841,7 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
827
841
|
// Promise.iter([ .. ]).iter(func)
|
|
828
842
|
// - func per element
|
|
829
843
|
// - func is called when an element is resolved/ready
|
|
830
|
-
// in
|
|
844
|
+
// in order of resolution/readiness
|
|
831
845
|
// Promise.seqiter([ .. ]).iter(func)
|
|
832
846
|
// - func per element
|
|
833
847
|
// - func is called when an element is resolved/ready
|
|
@@ -836,35 +850,35 @@ object.Constructor('IterablePromise', Promise, {
|
|
|
836
850
|
// NOTE: that here a promise will block handling of later promises even
|
|
837
851
|
// if they are resolved before it.
|
|
838
852
|
//
|
|
839
|
-
// XXX
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
//
|
|
847
|
-
//
|
|
848
|
-
// looks like we need to flatten things...
|
|
849
|
-
// XXX FIXED but need more testing...
|
|
853
|
+
// XXX is this correct???
|
|
854
|
+
// > g = function*(){ yield* [1,2,3] }
|
|
855
|
+
// // XXX should this expand the generator???
|
|
856
|
+
// > await Promise.seqiter([0, g()])
|
|
857
|
+
// -> [ 0, Object [Generator] {} ]
|
|
858
|
+
// > await Promise.seqiter([0, g()]).flat()
|
|
859
|
+
// -> [ 0, 1, 2, 3 ]
|
|
860
|
+
// XXX check if this behaves correctly (call order) on concatenation and
|
|
861
|
+
// other methods...
|
|
850
862
|
// XXX not sure if this is a viable strategy....
|
|
851
863
|
var IterableSequentialPromise =
|
|
852
864
|
module.IterableSequentialPromise =
|
|
853
865
|
object.Constructor('IterableSequentialPromise', IterablePromise, {
|
|
854
|
-
// XXX needs more work...
|
|
855
866
|
__pack: function(list, handler=undefined, onerror=undefined){
|
|
856
867
|
var seqiter = this.constructor
|
|
857
868
|
|
|
858
869
|
var repack = function(list){
|
|
859
870
|
var res = []
|
|
860
871
|
for(var [i, e] of list.entries()){
|
|
861
|
-
|
|
862
|
-
if(e instanceof Promise
|
|
872
|
+
if(e.then
|
|
863
873
|
// skip last promise -- nothing to wrap...
|
|
864
874
|
&& i < list.length-1){
|
|
865
875
|
res.push(e
|
|
866
876
|
.then(function(e){
|
|
867
|
-
|
|
877
|
+
// NOTE: this does not call any handlers, thus
|
|
878
|
+
// there should be no risk of out of order
|
|
879
|
+
// handler execution....
|
|
880
|
+
return seqiter(
|
|
881
|
+
[e, ...list.slice(i+1)])
|
|
868
882
|
.flat() }))
|
|
869
883
|
break }
|
|
870
884
|
res.push(e) }
|
|
@@ -875,15 +889,13 @@ object.Constructor('IterableSequentialPromise', IterablePromise, {
|
|
|
875
889
|
list = list instanceof SyncPromise ?
|
|
876
890
|
list.sync()
|
|
877
891
|
: list
|
|
892
|
+
// repack...
|
|
878
893
|
list = list instanceof Array ?
|
|
879
894
|
repack(list)
|
|
880
|
-
|
|
881
|
-
: list instanceof Promise ?
|
|
895
|
+
: list.then ?
|
|
882
896
|
list.then(repack)
|
|
883
897
|
: list
|
|
884
|
-
|
|
885
898
|
return handler ?
|
|
886
|
-
// XXX this seems to be wrong...
|
|
887
899
|
this.__handle(list, handler, onerror)
|
|
888
900
|
: list },
|
|
889
901
|
})
|
package/package.json
CHANGED
package/test.js
CHANGED
|
@@ -112,9 +112,9 @@ var cases = test.Cases({
|
|
|
112
112
|
},
|
|
113
113
|
|
|
114
114
|
IterablePromise: test.TestSet(function(){
|
|
115
|
-
var create = function(assert, value){
|
|
115
|
+
var create = function(assert, value, expected){
|
|
116
116
|
return {
|
|
117
|
-
input: value,
|
|
117
|
+
input: expected ?? value,
|
|
118
118
|
output: assert(Promise.iter(value), 'Promise.iter(', value, ')'),
|
|
119
119
|
} }
|
|
120
120
|
|
|
@@ -136,21 +136,17 @@ var cases = test.Cases({
|
|
|
136
136
|
array_mixed: function(assert){
|
|
137
137
|
return create(assert, [1, Promise.resolve(2), 3]) },
|
|
138
138
|
nested_array_mixed: function(assert){
|
|
139
|
-
return create(assert,
|
|
140
|
-
1,
|
|
141
|
-
|
|
142
|
-
[3],
|
|
143
|
-
Promise.resolve([4]),
|
|
144
|
-
]) },
|
|
139
|
+
return create(assert,
|
|
140
|
+
[1, Promise.resolve(2), [3], Promise.resolve([4])],
|
|
141
|
+
[1, 2, [3], [4]]) },
|
|
145
142
|
promise_array_mixed: function(assert){
|
|
146
|
-
return create(assert,
|
|
143
|
+
return create(assert,
|
|
144
|
+
Promise.resolve([1, Promise.resolve(2), 3]),
|
|
145
|
+
[1, 2, 3]) },
|
|
147
146
|
promise_nested_array_mixed: function(assert){
|
|
148
|
-
return create(assert,
|
|
149
|
-
1,
|
|
150
|
-
|
|
151
|
-
[3],
|
|
152
|
-
Promise.resolve([4]),
|
|
153
|
-
])) },
|
|
147
|
+
return create(assert,
|
|
148
|
+
Promise.resolve([1, Promise.resolve(2), [3], Promise.resolve([4])]),
|
|
149
|
+
[1, 2, [3], [4]]) },
|
|
154
150
|
})
|
|
155
151
|
this.Modifier({
|
|
156
152
|
nest: function(assert, setup){
|
|
@@ -182,6 +178,19 @@ var cases = test.Cases({
|
|
|
182
178
|
output: setup.output
|
|
183
179
|
.filter(function(e){ return false }),
|
|
184
180
|
} },
|
|
181
|
+
filter_promise_all: function(assert, setup){
|
|
182
|
+
setup.output = setup.output
|
|
183
|
+
.filter(function(e){
|
|
184
|
+
return Promise.resolve(true) })
|
|
185
|
+
return setup },
|
|
186
|
+
filter_promise_none: function(assert, setup){
|
|
187
|
+
return {
|
|
188
|
+
input: [],
|
|
189
|
+
output: setup.output
|
|
190
|
+
.filter(function(e){
|
|
191
|
+
return Promise.resolve(false) }),
|
|
192
|
+
} },
|
|
193
|
+
//*/
|
|
185
194
|
|
|
186
195
|
/* XXX need tuning...
|
|
187
196
|
concat_basic: function(assert, {input, output}){
|
|
@@ -266,32 +275,152 @@ var cases = test.Cases({
|
|
|
266
275
|
[1, 2, 3],
|
|
267
276
|
'promises as elements')
|
|
268
277
|
|
|
269
|
-
// XXX
|
|
270
|
-
var
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
// XXX split this into separate cases...
|
|
279
|
+
for(var meth of ['iter', 'seqiter']){
|
|
280
|
+
// XXX need a recursive assert...
|
|
281
|
+
var should_be = [ [1], [2], [3], [4], [5], [6] ]
|
|
282
|
+
var got = await Promise[meth]([
|
|
283
|
+
[1,1,1],
|
|
284
|
+
Promise.sync.resolve([2,2,2]),
|
|
285
|
+
Promise.resolve([3,3,3]),
|
|
286
|
+
Promise.iter([4,4,4]),
|
|
287
|
+
Promise.iter.resolve([5,5,5]),
|
|
288
|
+
Promise.all([6,6,6]),
|
|
289
|
+
],
|
|
290
|
+
function(e){
|
|
291
|
+
return e instanceof Array ?
|
|
292
|
+
[[ e[0] ]]
|
|
293
|
+
// XXX
|
|
294
|
+
: e })
|
|
295
|
+
assert(
|
|
296
|
+
should_be
|
|
297
|
+
.reduce(function(res, e, i){
|
|
298
|
+
//console.log('---', e, got[i])
|
|
299
|
+
if(res === false){
|
|
300
|
+
return false }
|
|
301
|
+
return e instanceof Array
|
|
302
|
+
&& got[i] instanceof Array
|
|
303
|
+
&& e.length == got[i].length
|
|
304
|
+
&& e[0] == got[i][0] }, true),
|
|
305
|
+
'pack/unpack', meth, got)
|
|
306
|
+
|
|
307
|
+
assert.array(
|
|
308
|
+
await Promise[meth]([1, Promise.resolve(2), Promise.resolve(3)]),
|
|
309
|
+
[1,2,3],
|
|
310
|
+
'flat unpack', meth)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
var order = []
|
|
314
|
+
await Promise.seqiter([
|
|
315
|
+
1,
|
|
316
|
+
Promise.resolve(2),
|
|
317
|
+
Promise.all([3,4]),
|
|
318
|
+
Promise.seqiter([5]),
|
|
319
|
+
6,
|
|
320
|
+
])
|
|
321
|
+
.flat()
|
|
322
|
+
.map(function(e){
|
|
323
|
+
order.push(e)
|
|
324
|
+
return e })
|
|
325
|
+
assert.array(
|
|
326
|
+
order,
|
|
327
|
+
[1,2,3,4,5,6],
|
|
328
|
+
'Promise.seqiter(..) handle order')
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
var test_async_handler = async function(input, output, handler, msg){
|
|
332
|
+
// sanity check...
|
|
333
|
+
assert.array(
|
|
334
|
+
await Promise.iter(input, handler),
|
|
335
|
+
output,
|
|
336
|
+
msg+' (sanity check -- fix first)')
|
|
337
|
+
assert.array(
|
|
338
|
+
await Promise.iter(input,
|
|
339
|
+
function(e){
|
|
340
|
+
return Promise.resolve(handler(e)) }),
|
|
341
|
+
await Promise.iter(input, handler),
|
|
342
|
+
msg) }
|
|
343
|
+
|
|
344
|
+
await test_async_handler(
|
|
345
|
+
[
|
|
346
|
+
1,
|
|
347
|
+
[2],
|
|
348
|
+
Promise.resolve(3),
|
|
349
|
+
Promise.resolve([4]),
|
|
278
350
|
],
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
351
|
+
[],
|
|
352
|
+
function(e){
|
|
353
|
+
return [] },
|
|
354
|
+
'handler returns promise (empty)')
|
|
355
|
+
await test_async_handler(
|
|
356
|
+
[
|
|
357
|
+
1,
|
|
358
|
+
[2],
|
|
359
|
+
Promise.resolve(3),
|
|
360
|
+
Promise.resolve([4]),
|
|
361
|
+
],
|
|
362
|
+
[ 'moo', 'moo', 'moo', 'moo' ],
|
|
363
|
+
function(e){
|
|
364
|
+
return ['moo'] },
|
|
365
|
+
'handler returns promise (array)')
|
|
366
|
+
await test_async_handler(
|
|
367
|
+
[
|
|
368
|
+
1,
|
|
369
|
+
[2],
|
|
370
|
+
Promise.resolve(3),
|
|
371
|
+
Promise.resolve([4]),
|
|
372
|
+
],
|
|
373
|
+
[ ['moo'], ['moo'], ['moo'], ['moo'] ],
|
|
374
|
+
function(e){
|
|
375
|
+
return [['moo']] },
|
|
376
|
+
'handler returns promise (array-array)')
|
|
377
|
+
|
|
378
|
+
// test STOP...
|
|
379
|
+
assert.array(
|
|
380
|
+
await Promise.iter([1,2,3,4], function(e){
|
|
381
|
+
if(e == 3){
|
|
382
|
+
throw Array.STOP }
|
|
383
|
+
return e }),
|
|
384
|
+
[1,2],
|
|
385
|
+
'.iter(..): STOP: basic')
|
|
386
|
+
assert.array(
|
|
387
|
+
await Promise.iter([1,2,Promise.resolve(3),4], function(e){
|
|
388
|
+
if(e == 3){
|
|
389
|
+
throw Array.STOP }
|
|
390
|
+
return e }),
|
|
391
|
+
// NOTE: 4 here is present as it was handled before the promise resolved...
|
|
392
|
+
[1,2,4],
|
|
393
|
+
'.iter(..): STOP: delayed')
|
|
394
|
+
assert.array(
|
|
395
|
+
await Promise.iter([1,2,Promise.resolve(3),4], function(e){
|
|
396
|
+
if(e == 3){
|
|
397
|
+
throw Array.STOP('stop') }
|
|
398
|
+
return e }),
|
|
399
|
+
// NOTE: 4 here is present as it was handled before the promise resolved...
|
|
400
|
+
[1,2,'stop',4],
|
|
401
|
+
'.iter(..): STOP(..): delayed')
|
|
402
|
+
assert.array(
|
|
403
|
+
await Promise.seqiter([1,2,3,4], function(e){
|
|
404
|
+
if(e == 3){
|
|
405
|
+
throw Array.STOP }
|
|
406
|
+
return e }),
|
|
407
|
+
[1,2],
|
|
408
|
+
'.seqiter(..): STOP: basic')
|
|
409
|
+
assert.array(
|
|
410
|
+
await Promise.seqiter([1,2,Promise.resolve(3),4], function(e){
|
|
411
|
+
if(e == 3){
|
|
412
|
+
throw Array.STOP }
|
|
413
|
+
return e }),
|
|
414
|
+
[1,2],
|
|
415
|
+
'.seqiter(..): STOP: delayed')
|
|
416
|
+
assert.array(
|
|
417
|
+
await Promise.seqiter([1,2,Promise.resolve(3),4], function(e){
|
|
418
|
+
if(e == 3){
|
|
419
|
+
throw Array.STOP('stop') }
|
|
420
|
+
return e }),
|
|
421
|
+
// NOTE: 4 here is present as it was handled before the promise resolved...
|
|
422
|
+
[1,2,'stop'],
|
|
423
|
+
'.seqiter(..): STOP(..): delayed')
|
|
295
424
|
},
|
|
296
425
|
|
|
297
426
|
// Date.js
|
|
@@ -549,8 +678,8 @@ Events.cases({
|
|
|
549
678
|
|
|
550
679
|
|
|
551
680
|
// test event list...
|
|
552
|
-
assert.array(obj.events, ['
|
|
553
|
-
assert.array(obj.eventful, ['
|
|
681
|
+
assert.array(obj.events, ['eventBlank', 'event'], '.events')
|
|
682
|
+
assert.array(obj.eventful, ['bareEventBlank', 'bareEvent'], '.eventful')
|
|
554
683
|
|
|
555
684
|
// bind...
|
|
556
685
|
var bind = function(evt){
|