2020-11-02 18:24:20 +03:00
|
|
|
/**********************************************************************
|
2021-01-05 04:49:24 +03:00
|
|
|
*
|
|
|
|
|
* This defines the following extensions to Promise:
|
|
|
|
|
*
|
|
|
|
|
* Promise.iter(seq)
|
2022-06-09 11:46:05 +03:00
|
|
|
* <promise>.iter()
|
2021-01-05 04:49:24 +03:00
|
|
|
* Iterable promise object.
|
2022-06-14 10:37:01 +03:00
|
|
|
* Similar to Promise.all(..) but adds basic iterator API.
|
2021-01-05 04:49:24 +03:00
|
|
|
*
|
|
|
|
|
* Promise.interactive(handler)
|
|
|
|
|
* Interactive promise object.
|
|
|
|
|
* This adds a basic message passing API to the promise.
|
|
|
|
|
*
|
|
|
|
|
* Promise.cooperative()
|
|
|
|
|
* Cooperative promise object.
|
|
|
|
|
* Exposes the API to resolve/reject the promise object
|
|
|
|
|
* externally.
|
|
|
|
|
*
|
2022-06-09 11:46:05 +03:00
|
|
|
* <promise>.as(obj)
|
|
|
|
|
* Promise proxy.
|
|
|
|
|
* Proxies the methods available from obj to promise value.
|
|
|
|
|
*
|
|
|
|
|
*
|
2021-01-05 04:49:24 +03:00
|
|
|
*
|
|
|
|
|
*
|
2020-11-02 18:24:20 +03:00
|
|
|
**********************************************/ /* c8 ignore next 2 */
|
|
|
|
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
|
|
|
|
(function(require){ var module={} // make module AMD/node compatible...
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
|
|
|
|
var object = require('ig-object')
|
|
|
|
|
|
2022-12-07 18:15:40 +03:00
|
|
|
var generator = require('./generator')
|
2022-06-17 12:01:04 +03:00
|
|
|
|
2021-05-27 08:46:51 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
2020-11-24 20:23:22 +03:00
|
|
|
// Iterable promise...
|
|
|
|
|
//
|
|
|
|
|
// Like Promise.all(..) but adds ability to iterate through results
|
|
|
|
|
// via generators .map(..)/.reduce(..) and friends...
|
|
|
|
|
//
|
2022-06-11 14:35:16 +03:00
|
|
|
// NOTE: the following can not be implemented here:
|
|
|
|
|
// .splice(..) - can't both modify and return
|
|
|
|
|
// a result...
|
|
|
|
|
// .pop() / .shift() - can't modify the promise, use
|
|
|
|
|
// .first() / .last() instead.
|
|
|
|
|
// [Symbol.iterator]() - needs to be sync and we can't
|
|
|
|
|
// know the number of elements to
|
|
|
|
|
// return promises before the whole
|
|
|
|
|
// iterable promise is resolved.
|
2022-06-09 11:15:20 +03:00
|
|
|
// NOTE: we are not using async/await here as we need to control the
|
|
|
|
|
// type of promise returned in cases where we know we are returning
|
|
|
|
|
// an array...
|
2022-06-12 10:28:01 +03:00
|
|
|
// NOTE: there is no point in implementing a 1:1 version of this that
|
|
|
|
|
// would not support element expansion/contraction as it would only
|
|
|
|
|
// simplify a couple of methods that are 1:1 (like .map(..) and
|
|
|
|
|
// .some(..)) while methods like .filter(..) will throw everything
|
|
|
|
|
// back to the complex IterablePromise...
|
2022-06-11 14:35:16 +03:00
|
|
|
//
|
|
|
|
|
// XXX how do we handle errors/rejections???
|
2022-06-14 10:37:01 +03:00
|
|
|
// ...mostly the current state is OK, but need more testing...
|
2022-06-17 12:01:04 +03:00
|
|
|
// XXX add support for async generators...
|
2022-06-11 19:35:35 +03:00
|
|
|
//
|
2022-06-11 15:49:05 +03:00
|
|
|
|
2022-06-09 11:12:39 +03:00
|
|
|
var iterPromiseProxy =
|
2022-06-15 03:44:24 +03:00
|
|
|
module.iterPromiseProxy =
|
2022-06-09 11:12:39 +03:00
|
|
|
function(name){
|
|
|
|
|
return function(...args){
|
|
|
|
|
return this.constructor(
|
|
|
|
|
this.then(function(lst){
|
|
|
|
|
return lst[name](...args) })) } }
|
2022-12-07 02:21:06 +03:00
|
|
|
|
2022-12-07 02:47:14 +03:00
|
|
|
// XXX ASYNC should this be async or simply return a SyncPromise/Promise???
|
2022-06-09 11:12:39 +03:00
|
|
|
var promiseProxy =
|
2022-06-15 03:44:24 +03:00
|
|
|
module.promiseProxy =
|
2022-06-09 11:12:39 +03:00
|
|
|
function(name){
|
|
|
|
|
return async function(...args){
|
|
|
|
|
return (await this)[name](...args) } }
|
|
|
|
|
|
2022-12-07 02:21:06 +03:00
|
|
|
|
2020-11-09 06:22:32 +03:00
|
|
|
var IterablePromise =
|
|
|
|
|
module.IterablePromise =
|
|
|
|
|
object.Constructor('IterablePromise', Promise, {
|
2022-06-13 19:43:45 +03:00
|
|
|
get STOP(){
|
|
|
|
|
return Array.STOP },
|
|
|
|
|
|
|
|
|
|
}, {
|
2022-06-11 14:35:16 +03:00
|
|
|
// packed array...
|
|
|
|
|
//
|
|
|
|
|
// Holds promise state.
|
2020-11-18 01:02:00 +03:00
|
|
|
//
|
|
|
|
|
// Format:
|
|
|
|
|
// [
|
2022-06-08 03:10:36 +03:00
|
|
|
// <non-array-value>,
|
2020-11-18 01:02:00 +03:00
|
|
|
// [ <value> ],
|
|
|
|
|
// <promise>,
|
|
|
|
|
// ...
|
|
|
|
|
// ]
|
|
|
|
|
//
|
2022-06-08 16:03:35 +03:00
|
|
|
// This format has several useful features:
|
2022-06-12 12:36:45 +03:00
|
|
|
// - concatenating packed lists results in a packed list
|
2022-06-08 16:03:35 +03:00
|
|
|
// - adding an iterable promise (as-is) into a packed list results
|
|
|
|
|
// in a packed list
|
|
|
|
|
//
|
2022-06-09 01:33:14 +03:00
|
|
|
// NOTE: in general iterable promises are implicitly immutable, so
|
2022-06-12 12:36:45 +03:00
|
|
|
// it is not recomended to ever edit this in-place...
|
|
|
|
|
// NOTE: we are not isolating or "protecting" any internals to
|
|
|
|
|
// enable users to responsibly extend the code.
|
2022-06-11 15:49:05 +03:00
|
|
|
__packed: null,
|
2020-11-10 01:58:51 +03:00
|
|
|
|
2022-06-11 15:49:05 +03:00
|
|
|
// low-level .__packed handlers/helpers...
|
2022-06-08 03:10:36 +03:00
|
|
|
//
|
2022-06-07 20:58:34 +03:00
|
|
|
// NOTE: these can be useful for debugging and extending...
|
2022-06-11 14:35:16 +03:00
|
|
|
//
|
|
|
|
|
// pack and oprionally transform/handle an array (sync)...
|
2022-06-13 13:26:31 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: if 'types/Array' is imported this will support throwing STOP
|
|
|
|
|
// from the handler.
|
|
|
|
|
// Due to the async nature of promises though the way stops are
|
|
|
|
|
// handled may be unpredictable -- the handlers can be run out
|
|
|
|
|
// of order, as the nested promises resolve and thus throwing
|
|
|
|
|
// stop will stop the handlers not yet run and not the next
|
|
|
|
|
// handlers in sequence.
|
|
|
|
|
// XXX EXPEREMENTAL: STOP...
|
2022-12-07 18:15:40 +03:00
|
|
|
// XXX ITER can we unwind (sync/async) generators one by one???
|
2022-12-19 22:43:27 +03:00
|
|
|
/* XXX this repeats part of the functionality of .__handle(..)
|
2022-12-07 23:41:26 +03:00
|
|
|
__pack: function(list, handler=undefined, onerror=undefined){
|
2022-06-08 03:10:36 +03:00
|
|
|
var that = this
|
2022-12-07 18:15:40 +03:00
|
|
|
// handle iterator...
|
|
|
|
|
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
|
|
|
if(typeof(list) == 'object'
|
|
|
|
|
&& Symbol.iterator in list){
|
2022-12-07 23:41:26 +03:00
|
|
|
if(!onerror){
|
|
|
|
|
list = [...list]
|
|
|
|
|
// handle errors in input generator...
|
2022-12-08 01:02:57 +03:00
|
|
|
// NOTE: this does not offer the most control because semantically
|
|
|
|
|
// we bust behave in the same manner as <generator>.iter(..)
|
2022-12-07 23:41:26 +03:00
|
|
|
} else {
|
|
|
|
|
var res = []
|
|
|
|
|
try{
|
|
|
|
|
for(var e of list){
|
|
|
|
|
res.push(e) }
|
|
|
|
|
}catch(err){
|
|
|
|
|
var r = onerror(err)
|
2022-12-08 01:02:57 +03:00
|
|
|
r !== undefined
|
|
|
|
|
&& res.push(r) }
|
|
|
|
|
list = res } }
|
2022-12-07 18:15:40 +03:00
|
|
|
// handle iterable promise...
|
2022-06-08 03:10:36 +03:00
|
|
|
if(list instanceof IterablePromise){
|
2022-12-07 23:41:26 +03:00
|
|
|
return this.__handle(list.__packed, handler, onerror) }
|
2022-12-07 18:15:40 +03:00
|
|
|
// handle promise / async-iterator...
|
|
|
|
|
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
2022-12-07 23:49:13 +03:00
|
|
|
if(typeof(list) == 'object'
|
|
|
|
|
&& Symbol.asyncIterator in list){
|
2022-12-07 23:41:26 +03:00
|
|
|
return list
|
|
|
|
|
.iter(handler, onerror)
|
|
|
|
|
.then(function(list){
|
2022-12-08 01:02:57 +03:00
|
|
|
return that.__pack(list) }) }
|
|
|
|
|
if(list instanceof Promise){
|
2022-12-07 23:49:13 +03:00
|
|
|
return list
|
|
|
|
|
.then(function(list){
|
|
|
|
|
return that.__pack(list, handler, onerror) }) }
|
2022-06-08 03:10:36 +03:00
|
|
|
// do the work...
|
|
|
|
|
// NOTE: packing and handling are mixed here because it's faster
|
|
|
|
|
// to do them both on a single list traverse...
|
2022-06-07 20:58:34 +03:00
|
|
|
var handle = !!handler
|
|
|
|
|
handler = handler
|
|
|
|
|
?? function(elem){
|
|
|
|
|
return [elem] }
|
2022-12-18 17:51:29 +03:00
|
|
|
// XXX this repeats .__handle(..), need to unify...
|
2022-06-13 03:27:47 +03:00
|
|
|
var stop = false
|
2022-06-13 13:26:31 +03:00
|
|
|
var map = 'map'
|
|
|
|
|
var pack = function(){
|
|
|
|
|
return [list].flat()
|
|
|
|
|
[map](function(elem){
|
2022-12-07 02:21:06 +03:00
|
|
|
// XXX EXPEREMENTAL...
|
|
|
|
|
return elem instanceof IterablePromise ?
|
|
|
|
|
(elem.isSync() ?
|
|
|
|
|
handler(elem.sync())
|
|
|
|
|
// XXX need to handle this but keep it IterablePromise...
|
|
|
|
|
: elem.iterthen(handler))
|
|
|
|
|
: (elem instanceof SyncPromise
|
|
|
|
|
&& !(elem.sync() instanceof Promise)) ?
|
|
|
|
|
handler(elem.sync())
|
|
|
|
|
: elem && elem.then ?
|
2022-12-07 23:41:26 +03:00
|
|
|
(handleSTOP ?
|
2022-06-13 13:32:35 +03:00
|
|
|
// stoppable -- need to handle stop async...
|
2022-06-13 13:26:31 +03:00
|
|
|
elem
|
|
|
|
|
.then(function(res){
|
|
|
|
|
return !stop ?
|
|
|
|
|
handler(res)
|
|
|
|
|
: [] })
|
|
|
|
|
// NOTE: we are using .catch(..) here
|
|
|
|
|
// instead of directly passing the
|
|
|
|
|
// error handler to be able to catch
|
|
|
|
|
// the STOP from the handler...
|
|
|
|
|
.catch(handleSTOP)
|
|
|
|
|
// non-stoppable...
|
|
|
|
|
: elem.then(handler))
|
|
|
|
|
: elem instanceof Array ?
|
|
|
|
|
handler(elem)
|
|
|
|
|
// NOTE: we keep things that do not need protecting
|
|
|
|
|
// from .flat() as-is...
|
|
|
|
|
: !handle ?
|
|
|
|
|
elem
|
|
|
|
|
: handler(elem) }) }
|
|
|
|
|
// pack (stoppable)...
|
2022-06-13 19:43:45 +03:00
|
|
|
if(!!this.constructor.STOP){
|
2022-06-13 13:26:31 +03:00
|
|
|
map = 'smap'
|
|
|
|
|
var handleSTOP = function(err){
|
2022-12-07 23:41:26 +03:00
|
|
|
// handle stop...
|
2022-06-13 13:26:31 +03:00
|
|
|
stop = err
|
2022-06-13 19:43:45 +03:00
|
|
|
if(err === that.constructor.STOP
|
|
|
|
|
|| err instanceof that.constructor.STOP){
|
2022-06-13 13:26:31 +03:00
|
|
|
return 'value' in err ?
|
|
|
|
|
err.value
|
|
|
|
|
: [] }
|
|
|
|
|
throw err }
|
|
|
|
|
try{
|
|
|
|
|
return pack()
|
|
|
|
|
}catch(err){
|
2022-06-13 13:32:35 +03:00
|
|
|
return handleSTOP(err) } }
|
2022-06-13 13:26:31 +03:00
|
|
|
|
|
|
|
|
// pack (non-stoppable)...
|
2022-06-13 13:32:35 +03:00
|
|
|
return pack() },
|
2022-12-13 23:52:56 +03:00
|
|
|
// transform/handle packed array (sync, but can return promises in the list)...
|
2022-12-07 23:41:26 +03:00
|
|
|
__handle: function(list, handler=undefined, onerror=undefined){
|
2022-06-07 20:58:34 +03:00
|
|
|
var that = this
|
|
|
|
|
if(typeof(list) == 'function'){
|
|
|
|
|
handler = list
|
2022-06-11 15:49:05 +03:00
|
|
|
list = this.__packed }
|
2022-06-07 20:58:34 +03:00
|
|
|
if(!handler){
|
|
|
|
|
return list }
|
|
|
|
|
// handle promise list...
|
|
|
|
|
if(list instanceof Promise){
|
|
|
|
|
return list.then(function(list){
|
2022-12-07 23:41:26 +03:00
|
|
|
return that.__handle(list, handler, onerror) }) }
|
2022-06-08 03:10:36 +03:00
|
|
|
// do the work...
|
|
|
|
|
// NOTE: since each section of the packed .__array is the same
|
|
|
|
|
// structure as the input we'll use .__pack(..) to handle
|
|
|
|
|
// them, this also keeps all the handling code in one place.
|
2022-06-13 19:43:45 +03:00
|
|
|
var map = !!this.constructor.STOP ?
|
2022-06-13 03:27:47 +03:00
|
|
|
'smap'
|
|
|
|
|
: 'map'
|
|
|
|
|
return list[map](function(elem){
|
2022-12-16 23:57:48 +03:00
|
|
|
elem = elem instanceof Array
|
|
|
|
|
|| elem instanceof Promise ?
|
2022-12-07 23:41:26 +03:00
|
|
|
that.__pack(elem, handler, onerror)
|
2022-12-16 23:57:48 +03:00
|
|
|
: [handler(elem)]
|
|
|
|
|
elem = elem instanceof Promise ?
|
|
|
|
|
elem.then(function([e]){
|
|
|
|
|
return e })
|
|
|
|
|
: elem
|
|
|
|
|
return elem })
|
2022-06-07 20:58:34 +03:00
|
|
|
.flat() },
|
2022-12-19 22:43:27 +03:00
|
|
|
/*/
|
|
|
|
|
__pack: function(list, handler=undefined, onerror=undefined){
|
|
|
|
|
var that = this
|
|
|
|
|
// handle iterator...
|
|
|
|
|
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
|
|
|
if(typeof(list) == 'object'
|
|
|
|
|
&& Symbol.iterator in list){
|
|
|
|
|
if(!onerror){
|
|
|
|
|
list = [...list]
|
|
|
|
|
// handle errors in input generator...
|
|
|
|
|
// NOTE: this does not offer the most control because semantically
|
|
|
|
|
// we bust behave in the same manner as <generator>.iter(..)
|
|
|
|
|
} else {
|
|
|
|
|
var res = []
|
|
|
|
|
try{
|
|
|
|
|
for(var e of list){
|
|
|
|
|
res.push(e) }
|
|
|
|
|
}catch(err){
|
|
|
|
|
var r = onerror(err)
|
|
|
|
|
r !== undefined
|
|
|
|
|
&& res.push(r) }
|
|
|
|
|
list = res } }
|
|
|
|
|
// handle iterable promise...
|
|
|
|
|
if(list instanceof IterablePromise){
|
|
|
|
|
return this.__handle(list.__packed, handler, onerror) }
|
|
|
|
|
// handle promise / async-iterator...
|
|
|
|
|
// XXX ITER do we unwind the iterator here or wait to unwind later???
|
|
|
|
|
if(typeof(list) == 'object'
|
|
|
|
|
&& Symbol.asyncIterator in list){
|
|
|
|
|
return list
|
|
|
|
|
.iter(handler, onerror)
|
|
|
|
|
.then(function(list){
|
|
|
|
|
return that.__pack(list) }) }
|
|
|
|
|
if(list instanceof Promise){
|
|
|
|
|
return list
|
|
|
|
|
.then(function(list){
|
|
|
|
|
return that.__pack(list, handler, onerror) }) }
|
|
|
|
|
// pack...
|
|
|
|
|
list = [list].flat()
|
|
|
|
|
.map(function(elem){
|
|
|
|
|
return elem instanceof Array ?
|
|
|
|
|
[elem]
|
|
|
|
|
: elem instanceof Promise ?
|
|
|
|
|
elem.then(function(e){
|
|
|
|
|
return [e] })
|
|
|
|
|
: elem })
|
|
|
|
|
// handle if needed...
|
|
|
|
|
return handler ?
|
|
|
|
|
this.__handle(list, handler, onerror)
|
|
|
|
|
: list },
|
|
|
|
|
// transform/handle packed array (sync, but can return promises in the list)...
|
2022-12-29 03:34:31 +03:00
|
|
|
// XXX need a strict spec...
|
2022-12-19 22:43:27 +03:00
|
|
|
__handle: function(list, handler=undefined, onerror=undefined){
|
|
|
|
|
var that = this
|
|
|
|
|
if(typeof(list) == 'function'){
|
|
|
|
|
handler = list
|
|
|
|
|
list = this.__packed }
|
|
|
|
|
if(!handler){
|
|
|
|
|
return list }
|
|
|
|
|
// handle promise list...
|
|
|
|
|
if(list instanceof Promise){
|
|
|
|
|
return list.then(function(list){
|
|
|
|
|
return that.__handle(list, handler, onerror) }) }
|
|
|
|
|
// do the work...
|
|
|
|
|
list = list instanceof Array ?
|
|
|
|
|
list
|
|
|
|
|
: [list]
|
|
|
|
|
var map = !!this.constructor.STOP ?
|
|
|
|
|
'smap'
|
|
|
|
|
: 'map'
|
2022-12-29 03:53:54 +03:00
|
|
|
var stop = false
|
2023-01-04 02:35:15 +03:00
|
|
|
// XXX do we handle generators here???
|
|
|
|
|
var each = function(elem){
|
|
|
|
|
return elem instanceof Array ?
|
|
|
|
|
elem
|
|
|
|
|
.map(handler)
|
|
|
|
|
.flat()
|
|
|
|
|
: handler(elem) }
|
2022-12-19 22:43:27 +03:00
|
|
|
return list
|
2022-12-29 03:53:54 +03:00
|
|
|
[map](
|
|
|
|
|
function(elem){
|
2022-12-29 15:57:56 +03:00
|
|
|
// NOTE: we are calling .flat() on the result so we
|
|
|
|
|
// need to keep a handled array as a single
|
|
|
|
|
// element by wrapping the return of handled(..)...
|
2022-12-29 03:53:54 +03:00
|
|
|
return elem instanceof IterablePromise ?
|
|
|
|
|
(elem.isSync() ?
|
2023-01-04 02:35:15 +03:00
|
|
|
each(elem.sync())
|
|
|
|
|
: elem.iterthen(each))
|
2022-12-29 15:53:16 +03:00
|
|
|
// sync sync promise...
|
2022-12-29 03:53:54 +03:00
|
|
|
: (elem instanceof SyncPromise
|
|
|
|
|
&& !(elem.sync() instanceof Promise)) ?
|
2023-01-04 02:35:15 +03:00
|
|
|
[each(elem.sync())]
|
2022-12-29 03:53:54 +03:00
|
|
|
// promise / promise-like...
|
|
|
|
|
: elem && elem.then ?
|
|
|
|
|
// NOTE: when this is explicitly stopped we
|
2022-12-29 15:53:16 +03:00
|
|
|
// do not call any more handlers after
|
|
|
|
|
// STOP is thrown/returned...
|
2022-12-29 15:57:56 +03:00
|
|
|
// NOTE: the promise protects this from .flat()
|
2022-12-29 03:53:54 +03:00
|
|
|
elem.then(function(elem){
|
|
|
|
|
return !stop ?
|
2023-01-04 02:35:15 +03:00
|
|
|
each(elem)
|
2022-12-29 03:53:54 +03:00
|
|
|
: [] })
|
|
|
|
|
: elem instanceof Array ?
|
2023-01-04 02:35:15 +03:00
|
|
|
[each(elem)]
|
|
|
|
|
: each(elem) },
|
2022-12-29 15:53:16 +03:00
|
|
|
// handle STOP...
|
|
|
|
|
function(){
|
2022-12-29 03:53:54 +03:00
|
|
|
stop = true })
|
2022-12-19 22:43:27 +03:00
|
|
|
.flat() },
|
|
|
|
|
//*/
|
2022-12-07 02:21:06 +03:00
|
|
|
// XXX this should return IterablePromise if .__packed is partially sync (???)
|
|
|
|
|
// unpack array (sync/async)...
|
|
|
|
|
__unpack: function(list){
|
|
|
|
|
list = list
|
|
|
|
|
?? this.__packed
|
|
|
|
|
// handle promise list...
|
|
|
|
|
if(list instanceof IterablePromise){
|
|
|
|
|
return list.__unpack() }
|
|
|
|
|
if(list instanceof Promise){
|
|
|
|
|
return list
|
|
|
|
|
.then(this.__unpack.bind(this)) }
|
|
|
|
|
var res = []
|
|
|
|
|
for(var e of list){
|
|
|
|
|
if(e instanceof IterablePromise){
|
|
|
|
|
e = e.__unpack() }
|
|
|
|
|
if(e instanceof SyncPromise){
|
|
|
|
|
e = e.sync() }
|
|
|
|
|
// give up on a sync solution...
|
|
|
|
|
if(e instanceof Promise){
|
|
|
|
|
// XXX can we return an IterablePromise???
|
2022-12-07 18:15:40 +03:00
|
|
|
// XXX is there a more elegant way to do this???
|
2022-12-07 02:21:06 +03:00
|
|
|
return Promise.all(list)
|
|
|
|
|
.then(function(list){
|
2022-12-07 18:15:40 +03:00
|
|
|
return list.flat() })
|
|
|
|
|
.iter() }
|
2022-12-07 02:21:06 +03:00
|
|
|
res.push(e) }
|
|
|
|
|
return res.flat() },
|
|
|
|
|
|
2022-08-19 18:02:40 +03:00
|
|
|
[Symbol.asyncIterator]: async function*(){
|
|
|
|
|
var list = this.__packed
|
|
|
|
|
if(list instanceof Promise){
|
2022-12-07 02:21:06 +03:00
|
|
|
yield* await this.__unpack(list)
|
2022-08-19 18:02:40 +03:00
|
|
|
return }
|
|
|
|
|
for await(var elem of list){
|
|
|
|
|
yield* elem instanceof Array ?
|
|
|
|
|
elem
|
|
|
|
|
: [elem] } },
|
2020-11-18 01:02:00 +03:00
|
|
|
|
2020-11-17 21:35:04 +03:00
|
|
|
// iterator methods...
|
|
|
|
|
//
|
|
|
|
|
// These will return a new IterablePromise instance...
|
|
|
|
|
//
|
|
|
|
|
// NOTE: these are different to Array's equivalents in that the handler
|
|
|
|
|
// is called not in the order of the elements but rather in order
|
|
|
|
|
// of promise resolution...
|
2022-06-01 16:15:30 +03:00
|
|
|
// NOTE: index of items is unknowable because items can expand and
|
2022-06-11 11:36:13 +03:00
|
|
|
// contract depending on handlers (e.g. .filter(..) can remove
|
2020-11-17 21:35:04 +03:00
|
|
|
// items)...
|
2020-11-10 01:58:51 +03:00
|
|
|
map: function(func){
|
2022-06-03 19:41:13 +03:00
|
|
|
return this.constructor(this,
|
2021-05-26 11:57:21 +03:00
|
|
|
function(e){
|
2022-06-06 12:29:13 +03:00
|
|
|
var res = func(e)
|
|
|
|
|
return res instanceof Promise ?
|
|
|
|
|
res.then(function(e){
|
|
|
|
|
return [e] })
|
|
|
|
|
: [res] }) },
|
2020-11-16 02:38:19 +03:00
|
|
|
filter: function(func){
|
2022-06-03 19:41:13 +03:00
|
|
|
return this.constructor(this,
|
2020-11-17 21:35:04 +03:00
|
|
|
function(e){
|
2022-06-06 12:29:13 +03:00
|
|
|
var res = func(e)
|
2022-06-08 03:10:36 +03:00
|
|
|
var _filter = function(elem){
|
|
|
|
|
return res ?
|
|
|
|
|
[elem]
|
|
|
|
|
: [] }
|
2022-06-06 12:29:13 +03:00
|
|
|
return res instanceof Promise ?
|
2022-06-08 03:10:36 +03:00
|
|
|
res.then(_filter)
|
|
|
|
|
: _filter(e) }) },
|
2022-06-01 16:15:30 +03:00
|
|
|
// NOTE: this does not return an iterable promise as we can't know
|
|
|
|
|
// what the user reduces to...
|
2022-06-02 12:05:37 +03:00
|
|
|
// NOTE: the items can be handled out of order because the nested
|
2022-06-08 03:54:14 +03:00
|
|
|
// promises can resolve in any order...
|
2022-06-02 12:05:37 +03:00
|
|
|
// NOTE: since order of execution can not be guaranteed there is no
|
2022-06-12 10:28:01 +03:00
|
|
|
// point in implementing .reduceRight(..) in the same way
|
|
|
|
|
// (see below)...
|
2021-05-26 11:57:21 +03:00
|
|
|
reduce: function(func, res){
|
2022-06-03 19:41:13 +03:00
|
|
|
return this.constructor(this,
|
2021-05-26 11:57:21 +03:00
|
|
|
function(e){
|
|
|
|
|
res = func(res, e)
|
|
|
|
|
return [] })
|
2020-11-17 21:35:04 +03:00
|
|
|
.then(function(){
|
|
|
|
|
return res }) },
|
2022-06-09 01:33:14 +03:00
|
|
|
|
2022-08-14 11:15:54 +03:00
|
|
|
// XXX BETWEEN...
|
|
|
|
|
between: function(func){
|
2022-08-14 13:38:37 +03:00
|
|
|
var i = 0
|
|
|
|
|
var j = 0
|
|
|
|
|
var prev
|
|
|
|
|
return this.constructor(this,
|
|
|
|
|
function(e){
|
|
|
|
|
return i++ > 0 ?
|
|
|
|
|
[
|
|
|
|
|
typeof(func) == 'function' ?
|
2022-08-14 13:46:22 +03:00
|
|
|
func.call(this, [prev, e], i, i + j++)
|
2022-08-14 13:38:37 +03:00
|
|
|
: func,
|
|
|
|
|
e,
|
|
|
|
|
]
|
|
|
|
|
: [e] }) },
|
2022-08-14 11:15:54 +03:00
|
|
|
|
2022-06-21 13:49:50 +03:00
|
|
|
// XXX .chain(..) -- see generator.chain(..)
|
|
|
|
|
|
2020-11-17 16:12:13 +03:00
|
|
|
flat: function(depth=1){
|
2022-06-03 19:41:13 +03:00
|
|
|
return this.constructor(this,
|
2021-05-26 11:57:21 +03:00
|
|
|
function(e){
|
2022-06-01 16:15:30 +03:00
|
|
|
return (depth > 1
|
|
|
|
|
&& e != null
|
|
|
|
|
&& e.flat) ?
|
|
|
|
|
e.flat(depth-1)
|
|
|
|
|
: depth != 0 ?
|
|
|
|
|
e
|
|
|
|
|
: [e] }) },
|
2022-06-02 12:05:37 +03:00
|
|
|
reverse: function(){
|
2022-06-11 15:49:05 +03:00
|
|
|
var lst = this.__packed
|
2022-06-03 19:41:13 +03:00
|
|
|
return this.constructor(
|
|
|
|
|
lst instanceof Promise ?
|
|
|
|
|
lst.then(function(elems){
|
|
|
|
|
return elems instanceof Array ?
|
|
|
|
|
elems.slice()
|
2022-06-02 12:05:37 +03:00
|
|
|
.reverse()
|
2022-06-03 19:41:13 +03:00
|
|
|
: elems })
|
|
|
|
|
: lst
|
|
|
|
|
.map(function(elems){
|
|
|
|
|
return elems instanceof Array ?
|
2022-06-03 21:23:23 +03:00
|
|
|
elems.slice()
|
|
|
|
|
.reverse()
|
|
|
|
|
: elems instanceof Promise ?
|
|
|
|
|
elems.then(function(elems){
|
|
|
|
|
return elems.reverse() })
|
2022-06-03 19:41:13 +03:00
|
|
|
: elems })
|
|
|
|
|
.reverse(),
|
|
|
|
|
'raw') },
|
2022-06-01 16:15:30 +03:00
|
|
|
|
2022-06-08 03:10:36 +03:00
|
|
|
// NOTE: the following methods can create an unresolved promise from
|
|
|
|
|
// a resolved promise...
|
2022-06-04 03:01:54 +03:00
|
|
|
concat: function(other){
|
2022-06-08 03:10:36 +03:00
|
|
|
var that = this
|
|
|
|
|
var cur = this.__pack(this)
|
|
|
|
|
var other = this.__pack(other)
|
2022-06-07 20:58:34 +03:00
|
|
|
return this.constructor(
|
2022-06-08 03:38:47 +03:00
|
|
|
// NOTE: we need to keep things as exposed as possible, this
|
|
|
|
|
// is why we're not blanketing all the cases with
|
|
|
|
|
// Promise.all(..)...
|
2022-06-08 03:10:36 +03:00
|
|
|
(cur instanceof Promise
|
|
|
|
|
&& other instanceof Promise) ?
|
2022-06-08 03:38:47 +03:00
|
|
|
[cur, other]
|
2022-06-08 03:10:36 +03:00
|
|
|
: cur instanceof Promise ?
|
2022-06-08 03:38:47 +03:00
|
|
|
[cur, ...other]
|
2022-06-08 03:10:36 +03:00
|
|
|
: other instanceof Promise ?
|
2022-06-08 03:38:47 +03:00
|
|
|
[...cur, other]
|
2022-06-08 03:45:06 +03:00
|
|
|
: [...cur, ...other],
|
2022-06-08 03:10:36 +03:00
|
|
|
'raw') },
|
2022-06-04 03:01:54 +03:00
|
|
|
push: function(elem){
|
|
|
|
|
return this.concat([elem]) },
|
|
|
|
|
unshift: function(elem){
|
|
|
|
|
return this.constructor([elem])
|
|
|
|
|
.concat(this) },
|
2020-11-18 01:02:00 +03:00
|
|
|
|
2022-06-11 14:35:16 +03:00
|
|
|
// proxy methods...
|
|
|
|
|
//
|
|
|
|
|
// These require the whole promise to resolve to trigger.
|
|
|
|
|
//
|
|
|
|
|
// An exception to this would be .at(0)/.first() and .at(-1)/.last()
|
|
|
|
|
// that can get the target element if it's accessible.
|
|
|
|
|
//
|
|
|
|
|
// NOTE: methods that are guaranteed to return an array will return
|
|
|
|
|
// an iterable promise (created with iterPromiseProxy(..))...
|
|
|
|
|
//
|
2022-12-07 02:47:14 +03:00
|
|
|
// XXX ASYNC should this be async or simply return a SyncPromise/Promise???
|
2022-06-09 01:33:14 +03:00
|
|
|
at: async function(i){
|
2022-06-11 15:49:05 +03:00
|
|
|
var list = this.__packed
|
2022-06-09 01:33:14 +03:00
|
|
|
return ((i != 0 && i != -1)
|
|
|
|
|
|| list instanceof Promise
|
2022-06-15 10:36:33 +03:00
|
|
|
// XXX not sure if this is correct...
|
2022-06-09 01:33:14 +03:00
|
|
|
|| list.at(i) instanceof Promise) ?
|
|
|
|
|
(await this).at(i)
|
|
|
|
|
// NOTE: we can only reason about first/last explicit elements,
|
2022-06-09 11:12:39 +03:00
|
|
|
// anything else is non-deterministic...
|
2022-06-09 01:33:14 +03:00
|
|
|
: list.at(i) instanceof Promise ?
|
|
|
|
|
[await list.at(i)].flat().at(i)
|
|
|
|
|
: list.at(i) instanceof Array ?
|
|
|
|
|
list.at(i).at(i)
|
|
|
|
|
: list.at(i) },
|
|
|
|
|
first: function(){
|
|
|
|
|
return this.at(0) },
|
|
|
|
|
last: function(){
|
|
|
|
|
return this.at(-1) },
|
2022-06-04 03:10:49 +03:00
|
|
|
|
2022-06-11 19:35:35 +03:00
|
|
|
// NOTE: unlike .reduce(..) this needs the parent fully resolved
|
|
|
|
|
// to be able to iterate from the end.
|
2022-06-13 19:43:45 +03:00
|
|
|
// XXX is it faster to do .reverse().reduce(..) ???
|
2022-06-11 19:35:35 +03:00
|
|
|
reduceRight: promiseProxy('reduceRight'),
|
|
|
|
|
|
2022-06-09 11:12:39 +03:00
|
|
|
// NOTE: there is no way we can do a sync generator returning
|
2022-06-13 20:10:09 +03:00
|
|
|
// promises for values because any promise in .__packed makes
|
|
|
|
|
// the value count/index non-deterministic...
|
2022-06-09 11:12:39 +03:00
|
|
|
sort: iterPromiseProxy('sort'),
|
|
|
|
|
slice: iterPromiseProxy('slice'),
|
2022-06-12 10:28:01 +03:00
|
|
|
|
2022-06-09 11:12:39 +03:00
|
|
|
entries: iterPromiseProxy('entries'),
|
|
|
|
|
keys: iterPromiseProxy('keys'),
|
|
|
|
|
values: iterPromiseProxy('values'),
|
|
|
|
|
|
|
|
|
|
indexOf: promiseProxy('indexOf'),
|
2022-06-11 19:35:35 +03:00
|
|
|
lastIndexOf: promiseProxy('lastIndexOf'),
|
2022-06-09 11:12:39 +03:00
|
|
|
includes: promiseProxy('includes'),
|
|
|
|
|
|
2022-06-13 22:48:07 +03:00
|
|
|
//
|
|
|
|
|
// .find(<func>)
|
|
|
|
|
// .find(<func>, 'value')
|
|
|
|
|
// -> <promise>(<value>)
|
|
|
|
|
//
|
|
|
|
|
// .find(<func>, 'result')
|
|
|
|
|
// -> <promise>(<result>)
|
|
|
|
|
//
|
|
|
|
|
// .find(<func>, 'bool')
|
|
|
|
|
// -> <promise>(<bool>)
|
|
|
|
|
//
|
|
|
|
|
// NOTE: this is slightly different to Array's .find(..) in that it
|
|
|
|
|
// accepts the result value enabling returning both the value
|
|
|
|
|
// itself ('value', default), the test function's result
|
|
|
|
|
// ('result') or true/false ('bool') -- this is added to be
|
|
|
|
|
// able to distinguish between the undefined as a stored value
|
|
|
|
|
// and undefined as a "nothing found" result.
|
2022-06-13 20:10:09 +03:00
|
|
|
// NOTE: I do not get how essentially identical methods .some(..)
|
2022-06-13 22:48:07 +03:00
|
|
|
// and .find(..) got added to JS's Array...
|
|
|
|
|
// the only benefit is that .some(..) handles undefined values
|
|
|
|
|
// stored in the array better...
|
2022-06-13 19:43:45 +03:00
|
|
|
// NOTE: this will return the result as soon as it's available but
|
2022-06-13 20:10:09 +03:00
|
|
|
// it will not stop the created but unresolved at the time
|
|
|
|
|
// promises from executing, this is both good and bad:
|
|
|
|
|
// + it will not break other clients waiting for promises
|
|
|
|
|
// to resolve...
|
|
|
|
|
// - if no clients are available this can lead to wasted
|
|
|
|
|
// CPU time...
|
2022-12-07 02:47:14 +03:00
|
|
|
//
|
|
|
|
|
// XXX ASYNC should this be async or simply return a SyncPromise/Promise???
|
2022-06-13 22:48:07 +03:00
|
|
|
find: async function(func, result='value'){
|
2022-06-13 19:43:45 +03:00
|
|
|
var that = this
|
2022-06-13 20:10:09 +03:00
|
|
|
// NOTE: not using pure await here as this is simpler to actually
|
|
|
|
|
// control the moment the resulting promise resolves without
|
|
|
|
|
// the need for juggling state...
|
|
|
|
|
return new Promise(function(resolve, reject){
|
|
|
|
|
var resolved = false
|
|
|
|
|
that.map(function(elem){
|
2022-06-13 22:48:07 +03:00
|
|
|
var res = func(elem)
|
|
|
|
|
if(res){
|
2022-06-13 20:10:09 +03:00
|
|
|
resolved = true
|
2022-06-13 22:48:07 +03:00
|
|
|
resolve(
|
|
|
|
|
result == 'bool' ?
|
|
|
|
|
true
|
|
|
|
|
: result == 'result' ?
|
|
|
|
|
res
|
|
|
|
|
: elem)
|
2022-06-13 20:10:09 +03:00
|
|
|
// XXX EXPEREMENTAL: STOP...
|
|
|
|
|
// NOTE: we do not need to throw STOP here
|
|
|
|
|
// but it can prevent some overhead...
|
|
|
|
|
if(that.constructor.STOP){
|
|
|
|
|
throw that.constructor.STOP } } })
|
|
|
|
|
.then(function(){
|
|
|
|
|
resolved
|
2022-06-13 22:48:07 +03:00
|
|
|
|| resolve(
|
|
|
|
|
result == 'bool' ?
|
|
|
|
|
false
|
|
|
|
|
: undefined) }) }) },
|
2022-06-13 20:10:09 +03:00
|
|
|
findIndex: promiseProxy('findIndex'),
|
|
|
|
|
|
2022-06-13 22:48:07 +03:00
|
|
|
// NOTE: this is just a special-case of .find(..)
|
2022-12-07 02:47:14 +03:00
|
|
|
// XXX ASYNC should this be async or simply return a SyncPromise/Promise???
|
2022-06-13 20:10:09 +03:00
|
|
|
some: async function(func){
|
2022-06-13 22:48:07 +03:00
|
|
|
return this.find(func, 'bool') },
|
2022-06-13 20:10:09 +03:00
|
|
|
every: promiseProxy('every'),
|
2022-06-09 01:33:14 +03:00
|
|
|
|
2020-11-18 01:02:00 +03:00
|
|
|
|
2022-12-07 02:47:14 +03:00
|
|
|
// XXX ASYNC should this be async or simply return a SyncPromise/Promise???
|
2022-07-27 14:11:01 +03:00
|
|
|
join: async function(){
|
|
|
|
|
return [...(await this)]
|
|
|
|
|
.join(...arguments) },
|
|
|
|
|
|
|
|
|
|
|
2022-06-15 10:36:33 +03:00
|
|
|
// this is defined globally as Promise.prototype.iter(..)
|
|
|
|
|
//
|
|
|
|
|
// for details see: PromiseMixin(..) below...
|
|
|
|
|
//iter: function(handler=undefined){ ... },
|
|
|
|
|
|
|
|
|
|
|
2022-06-11 14:35:16 +03:00
|
|
|
// promise api...
|
|
|
|
|
//
|
2020-11-18 01:02:00 +03:00
|
|
|
// Overload .then(..), .catch(..) and .finally(..) to return a plain
|
|
|
|
|
// Promise instnace...
|
|
|
|
|
//
|
|
|
|
|
// NOTE: .catch(..) and .finally(..) are implemented through .then(..)
|
|
|
|
|
// so we do not need to overload those...
|
2021-04-07 14:47:25 +03:00
|
|
|
// NOTE: this is slightly different from .then(..) in that it can be
|
|
|
|
|
// called without arguments and return a promise wrapper. This can
|
|
|
|
|
// be useful to hide special promise functionality...
|
2022-12-07 02:21:06 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: this is internally linked to the actual (via: ..then.call(this, ..))
|
|
|
|
|
// state and will be resolved in .__new__(..) below.
|
|
|
|
|
then: function(onfulfilled, onrejected){
|
2021-04-07 14:47:25 +03:00
|
|
|
var p = new Promise(
|
2020-11-18 01:02:00 +03:00
|
|
|
function(resolve, reject){
|
2020-11-24 05:48:54 +03:00
|
|
|
Promise.prototype.then.call(this,
|
2020-11-18 01:02:00 +03:00
|
|
|
// NOTE: resolve(..) / reject(..) return undefined so
|
|
|
|
|
// we can't pass them directly here...
|
|
|
|
|
function(res){
|
|
|
|
|
resolve(res)
|
|
|
|
|
return res },
|
|
|
|
|
function(res){
|
|
|
|
|
reject(res)
|
|
|
|
|
return res }) }.bind(this))
|
2021-04-07 14:47:25 +03:00
|
|
|
return arguments.length > 0 ?
|
|
|
|
|
p.then(...arguments)
|
|
|
|
|
: p },
|
2022-12-07 02:47:14 +03:00
|
|
|
// XXX EXPEREMENTAL revise...
|
2022-12-07 02:21:06 +03:00
|
|
|
// Like .then(..) but returns an IterablePromise instance...
|
|
|
|
|
iterthen: function(onfulfilled, onrejected){
|
|
|
|
|
if(this.isSync()){
|
|
|
|
|
var res = onfulfilled ?
|
|
|
|
|
this.constructor(onfulfilled(this.__unpack()))
|
|
|
|
|
: this.constructor(this.__unpack())
|
|
|
|
|
onrejected
|
|
|
|
|
&& res.catch(onrejected)
|
|
|
|
|
return res }
|
|
|
|
|
// XXX we need to feed the output of onfulfilled to the value of
|
|
|
|
|
// res, but to this without wrapping the whole thing in a
|
|
|
|
|
// promise (possible???)...
|
|
|
|
|
return arguments.length > 0 ?
|
|
|
|
|
this.constructor(this.then(...arguments))
|
|
|
|
|
: this.constructor(this.__packed, 'raw') },
|
|
|
|
|
|
|
|
|
|
// XXX EXPEREMENTAL
|
|
|
|
|
isSync: function(){
|
|
|
|
|
return !(this.__packed instanceof Promise
|
|
|
|
|
|| this.__packed
|
|
|
|
|
.filter(function(e){
|
|
|
|
|
return e instanceof IterablePromise ?
|
|
|
|
|
!e.isSync()
|
|
|
|
|
: e instanceof Promise
|
|
|
|
|
&& !(e instanceof SyncPromise) })
|
|
|
|
|
.length > 0) },
|
|
|
|
|
sync: function(error=false){
|
|
|
|
|
try{
|
|
|
|
|
var res = this.__unpack()
|
|
|
|
|
}catch(err){
|
|
|
|
|
if(error == false){
|
|
|
|
|
return }
|
|
|
|
|
if(typeof(error) == 'function'){
|
|
|
|
|
return error(err) }
|
|
|
|
|
throw err }
|
|
|
|
|
return error !== false
|
|
|
|
|
&& res instanceof Promise ?
|
|
|
|
|
// XXX should this be an IterablePromise???
|
|
|
|
|
res.catch(error)
|
|
|
|
|
: res },
|
2020-11-24 05:48:54 +03:00
|
|
|
|
2020-11-17 21:35:04 +03:00
|
|
|
|
2022-06-11 14:35:16 +03:00
|
|
|
// constructor...
|
2020-11-17 21:35:04 +03:00
|
|
|
//
|
|
|
|
|
// Promise.iter([ .. ])
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
// Promise.iter([ .. ], handler)
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// handler(e)
|
2022-06-09 22:49:43 +03:00
|
|
|
// -> [value, ..]
|
2020-11-16 02:38:19 +03:00
|
|
|
// -> []
|
2022-06-09 22:49:43 +03:00
|
|
|
// -> <promise>
|
2020-11-16 02:38:19 +03:00
|
|
|
//
|
2020-11-17 21:35:04 +03:00
|
|
|
//
|
2022-06-08 03:54:14 +03:00
|
|
|
// NOTE: element index is unknowable until the full list is expanded
|
2020-11-17 21:35:04 +03:00
|
|
|
// as handler(..)'s return value can expand to any number of
|
|
|
|
|
// items...
|
|
|
|
|
// XXX we can make the index a promise, then if the client needs
|
|
|
|
|
// the value they can wait for it...
|
2022-06-15 03:44:24 +03:00
|
|
|
// ...this may be quite an overhead...
|
2020-11-18 01:02:00 +03:00
|
|
|
//
|
|
|
|
|
//
|
2022-06-08 03:54:14 +03:00
|
|
|
// Special cases useful for extending this constructor...
|
2020-11-18 06:09:43 +03:00
|
|
|
//
|
2022-06-11 15:49:05 +03:00
|
|
|
// Set raw .__packed without any pre-processing...
|
2022-06-03 19:41:13 +03:00
|
|
|
// Promise.iter([ .. ], 'raw')
|
2020-11-18 01:02:00 +03:00
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
2022-06-03 21:23:23 +03:00
|
|
|
// Create a rejected iterator...
|
2020-11-18 01:02:00 +03:00
|
|
|
// Promise.iter(false)
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
2022-06-13 13:26:31 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: if 'types/Array' is imported this will support throwing STOP,
|
|
|
|
|
// for more info see notes for .__pack(..)
|
|
|
|
|
// XXX EXPEREMENTAL: STOP...
|
2022-12-07 23:41:26 +03:00
|
|
|
__new__: function(_, list, handler=undefined, onerror=undefined){
|
2020-11-10 01:58:51 +03:00
|
|
|
// instance...
|
2022-06-01 16:15:30 +03:00
|
|
|
var promise
|
2020-11-24 05:48:54 +03:00
|
|
|
var obj = Reflect.construct(
|
2023-01-03 04:13:15 +03:00
|
|
|
Promise,
|
|
|
|
|
//this.constructor.__proto__,
|
2020-11-24 05:48:54 +03:00
|
|
|
[function(resolve, reject){
|
2022-06-08 03:58:43 +03:00
|
|
|
// NOTE: this is here for Promise compatibility...
|
2020-11-10 01:58:51 +03:00
|
|
|
if(typeof(list) == 'function'){
|
2020-11-17 16:12:13 +03:00
|
|
|
return list.call(this, ...arguments) }
|
2020-11-18 01:02:00 +03:00
|
|
|
// initial reject...
|
|
|
|
|
if(list === false){
|
|
|
|
|
return reject() }
|
2020-11-17 16:12:13 +03:00
|
|
|
promise = {resolve, reject} }],
|
2023-01-03 02:43:03 +03:00
|
|
|
this.constructor)
|
2020-11-10 01:58:51 +03:00
|
|
|
|
2022-06-08 16:03:35 +03:00
|
|
|
// populate new instance...
|
2020-11-17 16:12:13 +03:00
|
|
|
if(promise){
|
2022-06-08 03:54:14 +03:00
|
|
|
// handle/pack input data...
|
2022-06-03 19:41:13 +03:00
|
|
|
if(handler != 'raw'){
|
2023-01-03 02:43:03 +03:00
|
|
|
//list = list instanceof IterablePromise ?
|
|
|
|
|
list = list instanceof this.constructor ?
|
2022-12-07 23:41:26 +03:00
|
|
|
obj.__handle(list.__packed, handler, onerror)
|
|
|
|
|
: obj.__pack(list, handler, onerror) }
|
2022-06-11 15:49:05 +03:00
|
|
|
Object.defineProperty(obj, '__packed', {
|
2020-11-17 21:35:04 +03:00
|
|
|
value: list,
|
|
|
|
|
enumerable: false,
|
2022-12-07 02:21:06 +03:00
|
|
|
// NOTE: this is needed for self-resolve...
|
|
|
|
|
writable: true,
|
2020-11-17 21:35:04 +03:00
|
|
|
})
|
2020-11-17 16:12:13 +03:00
|
|
|
// handle promise state...
|
2022-12-07 02:21:06 +03:00
|
|
|
try{
|
|
|
|
|
var res = obj.__unpack(list)
|
|
|
|
|
}catch(err){
|
|
|
|
|
promise.reject(err) }
|
|
|
|
|
res instanceof Promise ?
|
|
|
|
|
res
|
|
|
|
|
.then(function(list){
|
|
|
|
|
promise.resolve(list) })
|
|
|
|
|
.catch(promise.reject)
|
|
|
|
|
: promise.resolve(res)
|
|
|
|
|
// XXX EXPEREMENTAL
|
|
|
|
|
// XXX do we handle errors here???
|
|
|
|
|
// self-resolve state...
|
|
|
|
|
list instanceof Promise ?
|
|
|
|
|
list.then(function(list){
|
|
|
|
|
obj.__packed = list })
|
|
|
|
|
: list.forEach(function(elem, i){
|
|
|
|
|
elem instanceof Promise
|
|
|
|
|
&& elem.then(function(elem){
|
|
|
|
|
lst = obj.__packed.slice()
|
|
|
|
|
lst[i] = elem
|
|
|
|
|
obj.__packed = lst }) }) }
|
2020-11-10 01:58:51 +03:00
|
|
|
return obj },
|
2020-11-09 06:22:32 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-12-13 23:52:56 +03:00
|
|
|
//---------------------------------------------------------------------
|
2022-12-15 19:09:42 +03:00
|
|
|
// XXX EXPEREMENTAL/HACK...
|
2022-12-13 23:52:56 +03:00
|
|
|
|
2023-01-04 03:47:37 +03:00
|
|
|
// This like IterablePromise but guarantees handler execution in order
|
|
|
|
|
// element occurrence.
|
|
|
|
|
//
|
|
|
|
|
// For comparison:
|
|
|
|
|
// Promise.all([ .. ]).then(func)
|
|
|
|
|
// - func is called on element list
|
|
|
|
|
// - func is called when all the elements are resolved
|
|
|
|
|
// Promise.iter([ .. ]).iter(func)
|
|
|
|
|
// - func per element
|
|
|
|
|
// - func is called when an element is resolved/ready
|
|
|
|
|
// in any order
|
|
|
|
|
// Promise.seqiter([ .. ]).iter(func)
|
|
|
|
|
// - func per element
|
|
|
|
|
// - func is called when an element is resolved/ready
|
|
|
|
|
// and when all elements before it are handled
|
|
|
|
|
//
|
|
|
|
|
// NOTE: that here a promise will block handling of later promises even
|
|
|
|
|
// if they are resolved before it.
|
|
|
|
|
//
|
2023-01-04 05:34:41 +03:00
|
|
|
// XXX BUG (FIXED):
|
2023-01-04 03:47:37 +03:00
|
|
|
// await Promise.seqiter([
|
|
|
|
|
// 1,
|
|
|
|
|
// Promise.resolve(2),
|
2023-01-04 06:39:55 +03:00
|
|
|
// Promise.resolve(3),
|
|
|
|
|
// Promise.resolve(4),
|
|
|
|
|
// Promise.resolve(5),
|
2023-01-04 03:47:37 +03:00
|
|
|
// ])
|
|
|
|
|
// -> [ 1, 2, [3], [[4]], [[[5]]] ]
|
|
|
|
|
// looks like we need to flatten things...
|
|
|
|
|
// XXX FIXED but need more testing...
|
2023-01-04 06:39:55 +03:00
|
|
|
// XXX BUG (FIXED):
|
|
|
|
|
// await Promise.seqiter([
|
|
|
|
|
// [1],
|
|
|
|
|
// Promise.resolve([1]),
|
|
|
|
|
// Promise.resolve([1])
|
|
|
|
|
// ],
|
|
|
|
|
// e => [e])
|
|
|
|
|
// -> [ [ 1 ], 1, [ 1 ] ]
|
2023-01-04 05:34:41 +03:00
|
|
|
// XXX check if this behaves correctly (call order) on concatenation and
|
|
|
|
|
// other methods...
|
2022-12-17 19:00:13 +03:00
|
|
|
// XXX not sure if this is a viable strategy....
|
2022-12-13 23:52:56 +03:00
|
|
|
var IterableSequentialPromise =
|
|
|
|
|
module.IterableSequentialPromise =
|
|
|
|
|
object.Constructor('IterableSequentialPromise', IterablePromise, {
|
2023-01-02 22:16:23 +03:00
|
|
|
__pack: function(list, handler=undefined, onerror=undefined){
|
2023-01-03 02:43:03 +03:00
|
|
|
var seqiter = this.constructor
|
|
|
|
|
|
2023-01-03 04:13:15 +03:00
|
|
|
var repack = function(list){
|
2023-01-03 02:43:03 +03:00
|
|
|
var res = []
|
|
|
|
|
for(var [i, e] of list.entries()){
|
|
|
|
|
// XXX check for .then(..) instead???
|
2023-01-04 06:39:55 +03:00
|
|
|
//if(e instanceof Promise
|
|
|
|
|
if(e.then
|
2023-01-03 04:13:15 +03:00
|
|
|
// skip last promise -- nothing to wrap...
|
|
|
|
|
&& i < list.length-1){
|
|
|
|
|
res.push(e
|
|
|
|
|
.then(function(e){
|
2023-01-04 05:34:41 +03:00
|
|
|
return seqiter(
|
2023-01-04 06:39:55 +03:00
|
|
|
[e, ...list.slice(i+1)])
|
2023-01-04 03:47:37 +03:00
|
|
|
.flat() }))
|
2023-01-03 02:43:03 +03:00
|
|
|
break }
|
|
|
|
|
res.push(e) }
|
2023-01-03 04:13:15 +03:00
|
|
|
return res }
|
|
|
|
|
|
|
|
|
|
// NOTE: we are not handling the list here...
|
|
|
|
|
list = object.parentCall(IterableSequentialPromise.prototype.__pack, this, list)
|
2023-01-04 03:47:37 +03:00
|
|
|
list = list instanceof SyncPromise ?
|
|
|
|
|
list.sync()
|
|
|
|
|
: list
|
2023-01-03 04:13:15 +03:00
|
|
|
list = list instanceof Array ?
|
|
|
|
|
repack(list)
|
2023-01-03 02:43:03 +03:00
|
|
|
// XXX check for .then(..) instead???
|
2023-01-04 06:39:55 +03:00
|
|
|
//: list instanceof Promise ?
|
|
|
|
|
: list.then ?
|
2023-01-03 04:13:15 +03:00
|
|
|
list.then(repack)
|
|
|
|
|
: list
|
|
|
|
|
|
|
|
|
|
return handler ?
|
|
|
|
|
this.__handle(list, handler, onerror)
|
2023-01-03 02:43:03 +03:00
|
|
|
: list },
|
2022-12-13 23:52:56 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-11-24 05:48:54 +03:00
|
|
|
//---------------------------------------------------------------------
|
2020-11-24 20:23:22 +03:00
|
|
|
// Interactive promise...
|
|
|
|
|
//
|
|
|
|
|
// Adds ability to send messages to the running promise.
|
|
|
|
|
//
|
2020-11-24 05:48:54 +03:00
|
|
|
|
|
|
|
|
var InteractivePromise =
|
|
|
|
|
module.InteractivePromise =
|
|
|
|
|
object.Constructor('InteractivePromise', Promise, {
|
2021-04-07 03:32:23 +03:00
|
|
|
// XXX do we need a way to remove handlers???
|
2020-11-24 05:48:54 +03:00
|
|
|
__message_handlers: null,
|
|
|
|
|
|
|
|
|
|
send: function(...args){
|
|
|
|
|
var that = this
|
|
|
|
|
;(this.__message_handlers || [])
|
|
|
|
|
.forEach(function(h){ h.call(that, ...args) })
|
|
|
|
|
return this },
|
|
|
|
|
|
|
|
|
|
then: IterablePromise.prototype.then,
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Promise.interactive(handler)
|
|
|
|
|
// -> interacive-promise
|
|
|
|
|
//
|
|
|
|
|
// handler(resolve, reject, onmessage)
|
|
|
|
|
//
|
|
|
|
|
// onmessage(func)
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
__new__: function(_, handler){
|
|
|
|
|
var handlers = []
|
2021-04-07 03:32:23 +03:00
|
|
|
|
2020-11-24 05:48:54 +03:00
|
|
|
var onmessage = function(func){
|
2021-04-07 12:30:38 +03:00
|
|
|
// remove all handlers...
|
2021-04-07 03:32:23 +03:00
|
|
|
if(func === false){
|
2021-04-07 12:30:38 +03:00
|
|
|
var h = (obj == null ?
|
|
|
|
|
handlers
|
|
|
|
|
: (obj.__message_handlers || []))
|
|
|
|
|
h.splice(0, handlers.length)
|
|
|
|
|
// remove a specific handler...
|
2021-04-07 03:32:23 +03:00
|
|
|
} else if(arguments[1] === false){
|
|
|
|
|
var h = (obj == null ?
|
|
|
|
|
handlers
|
|
|
|
|
: (obj.__message_handlers || []))
|
|
|
|
|
h.splice(h.indexOf(func), 1)
|
|
|
|
|
// register a handler...
|
|
|
|
|
} else {
|
|
|
|
|
var h = obj == null ?
|
2022-06-11 16:04:56 +03:00
|
|
|
// NOTE: we need to get the handlers from
|
|
|
|
|
// .__message_handlers unless we are not
|
|
|
|
|
// fully defined yet, then use the bootstrap
|
|
|
|
|
// container (handlers)...
|
|
|
|
|
// ...since we can call onmessage(..) while
|
|
|
|
|
// the promise is still defined there is no
|
|
|
|
|
// way to .send(..) until it returns a promise
|
|
|
|
|
// object, this races here are highly unlikely...
|
2021-04-07 03:32:23 +03:00
|
|
|
handlers
|
|
|
|
|
: (obj.__message_handlers =
|
|
|
|
|
obj.__message_handlers ?? [])
|
|
|
|
|
handlers.push(func) } }
|
2020-11-24 05:48:54 +03:00
|
|
|
|
|
|
|
|
var obj = Reflect.construct(
|
|
|
|
|
InteractivePromise.__proto__,
|
|
|
|
|
!handler ?
|
|
|
|
|
[]
|
|
|
|
|
: [function(resolve, reject){
|
|
|
|
|
return handler(resolve, reject, onmessage) }],
|
|
|
|
|
InteractivePromise)
|
|
|
|
|
Object.defineProperty(obj, '__message_handlers', {
|
|
|
|
|
value: handlers,
|
|
|
|
|
enumerable: false,
|
2021-04-07 12:30:38 +03:00
|
|
|
// XXX should this be .configurable???
|
|
|
|
|
configurable: true,
|
2020-11-24 05:48:54 +03:00
|
|
|
})
|
|
|
|
|
return obj },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
2020-11-24 20:23:22 +03:00
|
|
|
// Cooperative promise...
|
|
|
|
|
//
|
|
|
|
|
// A promise that can be resolved/rejected externally.
|
2022-06-15 03:44:24 +03:00
|
|
|
//
|
2020-11-24 20:23:22 +03:00
|
|
|
// NOTE: normally this has no internal resolver logic...
|
|
|
|
|
//
|
2020-11-24 05:48:54 +03:00
|
|
|
|
|
|
|
|
var CooperativePromise =
|
|
|
|
|
module.CooperativePromise =
|
|
|
|
|
object.Constructor('CooperativePromise', Promise, {
|
|
|
|
|
__handlers: null,
|
|
|
|
|
|
|
|
|
|
get isSet(){
|
|
|
|
|
return this.__handlers === false },
|
|
|
|
|
|
|
|
|
|
set: function(value, resolve=true){
|
|
|
|
|
// can't set twice...
|
|
|
|
|
if(this.isSet){
|
|
|
|
|
throw new Error('.set(..): can not set twice') }
|
|
|
|
|
// bind to promise...
|
|
|
|
|
if(value && value.then && value.catch){
|
|
|
|
|
value.then(handlers.resolve)
|
|
|
|
|
value.catch(handlers.reject)
|
|
|
|
|
// resolve with value...
|
|
|
|
|
} else {
|
|
|
|
|
resolve ?
|
|
|
|
|
this.__handlers.resolve(value)
|
|
|
|
|
: this.__handlers.reject(value) }
|
|
|
|
|
// cleanup and prevent setting twice...
|
|
|
|
|
this.__handlers = false
|
|
|
|
|
return this },
|
|
|
|
|
|
|
|
|
|
then: IterablePromise.prototype.then,
|
|
|
|
|
|
|
|
|
|
__new__: function(){
|
|
|
|
|
var handlers
|
|
|
|
|
var resolver = arguments[1]
|
|
|
|
|
|
|
|
|
|
var obj = Reflect.construct(
|
|
|
|
|
CooperativePromise.__proto__,
|
|
|
|
|
[function(resolve, reject){
|
|
|
|
|
handlers = {resolve, reject}
|
|
|
|
|
// NOTE: this is here to support builtin .then(..)
|
|
|
|
|
resolver
|
|
|
|
|
&& resolver(resolve, reject) }],
|
|
|
|
|
CooperativePromise)
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(obj, '__handlers', {
|
|
|
|
|
value: handlers,
|
|
|
|
|
enumerable: false,
|
|
|
|
|
writable: true,
|
|
|
|
|
})
|
|
|
|
|
return obj },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-11-22 23:50:05 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
2022-05-30 12:12:00 +03:00
|
|
|
// XXX EXPEREMENTAL...
|
2022-05-30 12:07:28 +03:00
|
|
|
var ProxyPromise =
|
|
|
|
|
module.ProxyPromise =
|
|
|
|
|
object.Constructor('ProxyPromise', Promise, {
|
2022-06-11 16:04:56 +03:00
|
|
|
|
|
|
|
|
then: IterablePromise.prototype.then,
|
|
|
|
|
|
2022-06-12 10:28:01 +03:00
|
|
|
__new__: function(context, other, nooverride=false){
|
|
|
|
|
var proto = 'prototype' in other ?
|
|
|
|
|
other.prototype
|
|
|
|
|
: other
|
2022-05-30 12:07:28 +03:00
|
|
|
var obj = Reflect.construct(
|
|
|
|
|
ProxyPromise.__proto__,
|
|
|
|
|
[function(resolve, reject){
|
|
|
|
|
context.then(resolve)
|
|
|
|
|
context.catch(reject) }],
|
|
|
|
|
ProxyPromise)
|
|
|
|
|
// populate...
|
|
|
|
|
// NOTE: we are not using object.deepKeys(..) here as we need
|
|
|
|
|
// the key origin not to trigger property getters...
|
|
|
|
|
var seen = new Set()
|
2022-06-12 10:28:01 +03:00
|
|
|
nooverride = nooverride instanceof Array ?
|
|
|
|
|
new Set(nooverride)
|
|
|
|
|
: nooverride
|
2022-05-30 12:07:28 +03:00
|
|
|
while(proto != null){
|
|
|
|
|
Object.entries(Object.getOwnPropertyDescriptors(proto))
|
|
|
|
|
.forEach(function([key, value]){
|
|
|
|
|
// skip overloaded keys...
|
|
|
|
|
if(seen.has(key)){
|
|
|
|
|
return }
|
|
|
|
|
// skip non-functions...
|
|
|
|
|
if(typeof(value.value) != 'function'){
|
|
|
|
|
return }
|
|
|
|
|
// skip non-enumerable except for Object.prototype.run(..)...
|
|
|
|
|
if(!(key == 'run'
|
|
|
|
|
&& Object.prototype.run === value.value)
|
|
|
|
|
&& !value.enumerable){
|
|
|
|
|
return }
|
2022-06-12 10:28:01 +03:00
|
|
|
// do not override existing methods...
|
|
|
|
|
if(nooverride === true ?
|
|
|
|
|
key in obj
|
|
|
|
|
: nooverride instanceof Set ?
|
|
|
|
|
nooverride.has(key)
|
|
|
|
|
: nooverride){
|
|
|
|
|
return }
|
2022-05-30 12:07:28 +03:00
|
|
|
// proxy...
|
2022-06-09 11:12:39 +03:00
|
|
|
obj[key] = promiseProxy(key) })
|
2022-05-30 12:07:28 +03:00
|
|
|
proto = proto.__proto__ }
|
|
|
|
|
return obj },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-12-03 13:17:11 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
2022-12-04 18:30:54 +03:00
|
|
|
// XXX should this support async generators???
|
2022-12-04 15:58:28 +03:00
|
|
|
var syncAllProxy =
|
|
|
|
|
function(name){
|
|
|
|
|
return function(lst){
|
|
|
|
|
var sync = true
|
|
|
|
|
for(var e of lst){
|
|
|
|
|
if(e instanceof Promise
|
|
|
|
|
&& !(e instanceof SyncPromise)){
|
|
|
|
|
sync = false
|
|
|
|
|
break } }
|
|
|
|
|
return sync ?
|
|
|
|
|
this.resolve(lst)
|
|
|
|
|
: Promise[name](lst) } }
|
|
|
|
|
|
|
|
|
|
// XXX REVISE/TEST...
|
2022-12-04 18:30:54 +03:00
|
|
|
// XXX should this support async generators???
|
2022-12-04 15:59:07 +03:00
|
|
|
var syncAnyProxy =
|
2022-12-04 15:58:28 +03:00
|
|
|
function(name){
|
|
|
|
|
return function(lst){
|
|
|
|
|
for(var e of lst){
|
|
|
|
|
if(e instanceof SyncPromise
|
|
|
|
|
&& !('error' in e)){
|
|
|
|
|
return e }
|
|
|
|
|
if(!(e instanceof Promise)){
|
|
|
|
|
return this.resolve(e) } }
|
|
|
|
|
return Promise[name](list) } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
|
|
2022-12-04 05:01:34 +03:00
|
|
|
// XXX EXPEREMENTAL...
|
|
|
|
|
// XXX DOCS...
|
2022-12-03 13:17:11 +03:00
|
|
|
// XXX like promise but if a value can be generated sync then this will
|
|
|
|
|
// run in sync otherwise it will fall back to being a promise...
|
2022-12-04 05:10:22 +03:00
|
|
|
// XXX should we throw errors in sync mode??? ...option???
|
2022-12-04 05:29:37 +03:00
|
|
|
var SyncPromise =
|
|
|
|
|
module.SyncPromise =
|
|
|
|
|
object.Constructor('SyncPromise', Promise, {
|
2022-12-04 15:58:28 +03:00
|
|
|
// NOTE: we need to overload these as the builtin versions sneak-in
|
|
|
|
|
// async-ness before we can catch it in .__new__(..)
|
|
|
|
|
resolve: function(value){
|
|
|
|
|
return new this(function(resolve){ resolve(value) }) },
|
|
|
|
|
reject: function(error){
|
|
|
|
|
return new this(function(_, reject){ reject(error) }) },
|
2022-12-04 18:30:54 +03:00
|
|
|
// NOTE: these essentially add a special case where the inputs are
|
|
|
|
|
// either not promises or SyncPromise instances...
|
2022-12-04 15:58:28 +03:00
|
|
|
all: syncAllProxy('all'),
|
|
|
|
|
allSettled: syncAllProxy('allSettled'),
|
|
|
|
|
any: syncAnyProxy('any'),
|
|
|
|
|
race: syncAnyProxy('race'),
|
|
|
|
|
},{
|
2022-12-04 05:01:34 +03:00
|
|
|
//value: undefined,
|
2022-12-04 05:10:22 +03:00
|
|
|
//error: undefined,
|
2022-12-04 05:01:34 +03:00
|
|
|
|
2022-12-04 05:10:22 +03:00
|
|
|
// NOTE: if this is called it means that .__new__(..) returned in sync
|
|
|
|
|
// mode and thus set .value and possibly .error, soe there is no
|
|
|
|
|
// need to check for .value...
|
2022-12-03 13:17:11 +03:00
|
|
|
then: function(resolve, reject){
|
2022-12-04 17:01:15 +03:00
|
|
|
return this.hasOwnProperty('error') ?
|
|
|
|
|
this.constructor.reject(
|
|
|
|
|
reject ?
|
|
|
|
|
reject(this.error)
|
2022-12-04 17:02:01 +03:00
|
|
|
: this.error)
|
2022-12-04 17:01:15 +03:00
|
|
|
: resolve ?
|
|
|
|
|
this.constructor.resolve(
|
|
|
|
|
resolve(this.value))
|
|
|
|
|
// XXX should we return a copy???
|
|
|
|
|
: this },
|
2022-12-07 02:21:06 +03:00
|
|
|
sync: function(error='throw'){
|
|
|
|
|
if(error !== false
|
|
|
|
|
&& 'error' in this){
|
|
|
|
|
if(typeof(error) != 'function'){
|
|
|
|
|
throw this.error }
|
|
|
|
|
return error(this.error) }
|
|
|
|
|
return this.value },
|
2022-12-04 05:01:34 +03:00
|
|
|
|
2022-12-04 05:10:22 +03:00
|
|
|
// NOTE: if func calls resolve(..) with a promise then this will return
|
|
|
|
|
// that promise...
|
2022-12-03 13:17:11 +03:00
|
|
|
__new__: function(context, func){
|
2022-12-04 15:58:28 +03:00
|
|
|
var value
|
2022-12-03 13:17:11 +03:00
|
|
|
var resolve = function(res){
|
2022-12-04 15:58:28 +03:00
|
|
|
return (value = res) }
|
2022-12-04 05:01:34 +03:00
|
|
|
var rejected
|
2022-12-03 13:17:11 +03:00
|
|
|
var error
|
|
|
|
|
var reject = function(err){
|
2022-12-04 05:01:34 +03:00
|
|
|
rejected = true
|
2022-12-04 15:58:28 +03:00
|
|
|
return (error = err) }
|
2022-12-04 05:01:34 +03:00
|
|
|
// call...
|
2022-12-03 13:17:11 +03:00
|
|
|
try{
|
|
|
|
|
func(resolve, reject)
|
|
|
|
|
}catch(err){
|
|
|
|
|
reject(err) }
|
|
|
|
|
// async...
|
2022-12-04 05:01:34 +03:00
|
|
|
if(!error
|
2022-12-04 15:58:28 +03:00
|
|
|
&& value instanceof Promise){
|
2022-12-07 18:15:40 +03:00
|
|
|
return object.ASIS(value) }
|
2022-12-03 13:17:11 +03:00
|
|
|
// sync...
|
2022-12-04 15:58:28 +03:00
|
|
|
var obj = Promise.resolve(value)
|
|
|
|
|
obj.value = value
|
2022-12-04 05:01:34 +03:00
|
|
|
rejected
|
|
|
|
|
&& (obj.error = error)
|
|
|
|
|
return obj },
|
2022-12-03 13:17:11 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2022-12-07 02:21:06 +03:00
|
|
|
|
2022-05-30 12:07:28 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
2020-11-22 23:50:05 +03:00
|
|
|
var PromiseMixin =
|
|
|
|
|
module.PromiseMixin =
|
|
|
|
|
object.Mixin('PromiseMixin', 'soft', {
|
|
|
|
|
iter: IterablePromise,
|
2022-12-13 23:52:56 +03:00
|
|
|
// XXX
|
|
|
|
|
seqiter: IterableSequentialPromise,
|
|
|
|
|
|
2020-11-24 05:48:54 +03:00
|
|
|
interactive: InteractivePromise,
|
|
|
|
|
cooperative: CooperativePromise,
|
2022-12-13 23:52:56 +03:00
|
|
|
|
2022-12-04 05:29:37 +03:00
|
|
|
sync: SyncPromise,
|
2022-12-13 23:52:56 +03:00
|
|
|
|
2022-12-04 17:01:15 +03:00
|
|
|
// XXX should this be implemented via SyncPromise???
|
2022-12-08 01:28:01 +03:00
|
|
|
// XXX not sure if we need to expand async generators...
|
2022-12-08 01:41:39 +03:00
|
|
|
// (update README if this changes)
|
2022-12-07 23:41:26 +03:00
|
|
|
awaitOrRun: function(data, func, error){
|
2022-11-29 18:39:23 +03:00
|
|
|
data = [...arguments]
|
|
|
|
|
func = data.pop()
|
2022-12-03 13:17:11 +03:00
|
|
|
if(typeof(data.at(-1)) == 'function'){
|
|
|
|
|
error = func
|
|
|
|
|
func = data.pop() }
|
2022-12-08 01:28:01 +03:00
|
|
|
error = error ?
|
|
|
|
|
[error]
|
|
|
|
|
: []
|
|
|
|
|
// check if we need to await...
|
|
|
|
|
return data.reduce(function(res, e){
|
2022-11-29 18:39:23 +03:00
|
|
|
return res
|
|
|
|
|
|| e instanceof Promise }, false) ?
|
2022-12-08 01:28:01 +03:00
|
|
|
// NOTE: we will not reach this on empty data...
|
|
|
|
|
(data.length > 1 ?
|
|
|
|
|
Promise.all(data)
|
|
|
|
|
.then(
|
|
|
|
|
function(res){
|
|
|
|
|
return func(...res) },
|
|
|
|
|
...error)
|
|
|
|
|
: data[0].then(func, ...error))
|
|
|
|
|
// XXX not sure if we need to expand async generators...
|
2022-12-08 01:38:06 +03:00
|
|
|
// ...since it has .then(..) it can be treated as a promise...
|
|
|
|
|
// XXX should we just check for .then(..) ???
|
2022-12-08 01:41:39 +03:00
|
|
|
// XXX update README if this changes...
|
2022-12-08 01:28:01 +03:00
|
|
|
: (data.length == 1
|
|
|
|
|
&& Symbol.asyncIterator in data[0]
|
|
|
|
|
&& 'then' in data[0]) ?
|
|
|
|
|
data[0].then(func, ...error)
|
|
|
|
|
: error.length > 0 ?
|
2022-12-03 13:17:11 +03:00
|
|
|
function(){
|
|
|
|
|
try{
|
2022-12-03 13:20:29 +03:00
|
|
|
return func(...data)
|
2022-12-03 13:17:11 +03:00
|
|
|
}catch(err){
|
2022-12-08 01:28:01 +03:00
|
|
|
return error[0](err) } }()
|
2022-11-29 18:39:23 +03:00
|
|
|
: func(...data) },
|
2020-11-22 23:50:05 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
PromiseMixin(Promise)
|
2022-06-01 16:15:30 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
var PromiseProtoMixin =
|
|
|
|
|
module.PromiseProtoMixin =
|
|
|
|
|
object.Mixin('PromiseProtoMixin', 'soft', {
|
|
|
|
|
as: ProxyPromise,
|
2022-12-13 23:52:56 +03:00
|
|
|
|
2022-12-07 23:41:26 +03:00
|
|
|
iter: function(handler=undefined, onerror=undefined){
|
|
|
|
|
return IterablePromise(this, handler, onerror) },
|
2022-12-13 23:52:56 +03:00
|
|
|
// XXX
|
|
|
|
|
seqiter: function(handler=undefined, onerror=undefined){
|
|
|
|
|
return IterableSequentialPromise(this, handler, onerror) },
|
|
|
|
|
|
2022-12-07 18:15:40 +03:00
|
|
|
// unify the general promise API with other promise types...
|
|
|
|
|
// XXX should this be here???
|
|
|
|
|
// XXX error if given must return the result... (???)
|
|
|
|
|
sync: function(error){
|
|
|
|
|
typeof(error) == 'function'
|
|
|
|
|
&& this.catch(error)
|
2022-12-04 05:29:37 +03:00
|
|
|
return this },
|
2022-06-01 16:15:30 +03:00
|
|
|
})
|
|
|
|
|
|
2022-05-30 12:07:28 +03:00
|
|
|
PromiseProtoMixin(Promise.prototype)
|
|
|
|
|
|
2020-11-22 23:50:05 +03:00
|
|
|
|
|
|
|
|
|
2020-11-02 18:24:20 +03:00
|
|
|
|
|
|
|
|
/**********************************************************************
|
2022-06-11 13:53:24 +03:00
|
|
|
* vim:set ts=4 sw=4 nowrap : */ return module })
|