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')
|
|
|
|
|
|
|
|
|
|
|
2020-11-17 16:12:13 +03:00
|
|
|
|
2020-11-02 18:24:20 +03:00
|
|
|
/*********************************************************************/
|
|
|
|
|
|
2020-11-09 06:22:32 +03:00
|
|
|
var IterablePromise =
|
|
|
|
|
module.IterablePromise =
|
|
|
|
|
object.Constructor('IterablePromise', Promise, {
|
2020-11-18 01:02:00 +03:00
|
|
|
//
|
|
|
|
|
// Format:
|
|
|
|
|
// [
|
|
|
|
|
// [ <value> ],
|
|
|
|
|
// <promise>,
|
|
|
|
|
// ...
|
|
|
|
|
// ]
|
|
|
|
|
//
|
2020-11-10 01:58:51 +03:00
|
|
|
__list: null,
|
|
|
|
|
|
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...
|
|
|
|
|
//
|
2020-11-18 01:02:00 +03:00
|
|
|
// When called from a resolved promise these will return a new
|
|
|
|
|
// resolved promise with updated values...
|
|
|
|
|
//
|
|
|
|
|
// When called from a rejected promise these will return a rejected
|
|
|
|
|
// with the same reason promise...
|
|
|
|
|
//
|
|
|
|
|
//
|
2020-11-17 21:35:04 +03:00
|
|
|
// 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...
|
|
|
|
|
// NOTE: index of items is unknowable because items can expand on
|
|
|
|
|
// contract depending on handlrs (e.g. .filter(..) can remove
|
|
|
|
|
// items)...
|
2020-11-18 01:02:00 +03:00
|
|
|
// This the following can not be implemented here:
|
|
|
|
|
// .slice(..)
|
|
|
|
|
// .splice(..)
|
|
|
|
|
// .at(..)
|
|
|
|
|
// [Symbol.iterator]() - needs to be sync...
|
|
|
|
|
// The followng methods are questionable:
|
|
|
|
|
// .indexOf(..)
|
|
|
|
|
// .includes(..)
|
|
|
|
|
// .some(..) / .every(..)
|
|
|
|
|
// .sort(..)
|
2020-11-10 01:58:51 +03:00
|
|
|
map: function(func){
|
2020-11-17 21:35:04 +03:00
|
|
|
return this.constructor(this.__list, function(e){
|
|
|
|
|
return [func(e)] }) },
|
2020-11-16 02:38:19 +03:00
|
|
|
filter: function(func){
|
2020-11-17 21:35:04 +03:00
|
|
|
return this.constructor(this.__list, function(e){
|
|
|
|
|
return func(e) ?
|
2020-11-16 02:38:19 +03:00
|
|
|
[e]
|
|
|
|
|
: [] }) },
|
2020-11-17 16:12:13 +03:00
|
|
|
reduce: function(func, res){
|
2020-11-17 21:35:04 +03:00
|
|
|
return this.constructor(this.__list,
|
|
|
|
|
function(e){
|
|
|
|
|
res = func(res, e)
|
|
|
|
|
return [] })
|
|
|
|
|
.then(function(){
|
|
|
|
|
return res }) },
|
2020-11-17 16:12:13 +03:00
|
|
|
flat: function(depth=1){
|
2020-11-17 21:35:04 +03:00
|
|
|
return this.constructor(this.__list, function(e){
|
2020-11-17 16:12:13 +03:00
|
|
|
return (e && e.flat) ?
|
|
|
|
|
e.flat(depth)
|
|
|
|
|
: e }) },
|
|
|
|
|
|
2020-11-10 01:58:51 +03:00
|
|
|
|
2020-11-18 01:02:00 +03:00
|
|
|
// XXX do we need:
|
|
|
|
|
// .pop()
|
|
|
|
|
// .shift()
|
|
|
|
|
// .first() / .last()
|
|
|
|
|
// XXX these can change the "resolved" state...
|
|
|
|
|
// ...i.e. return a pending promise when called from a fulfilled
|
|
|
|
|
// promise....
|
|
|
|
|
// .concat(..)
|
|
|
|
|
// .push(..)
|
|
|
|
|
// .unshift(..)
|
|
|
|
|
// .first(..) / .last(..)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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...
|
2020-11-24 05:48:54 +03:00
|
|
|
then: function (onfulfilled, onrejected){
|
2020-11-18 01:02:00 +03:00
|
|
|
return new Promise(
|
|
|
|
|
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))
|
|
|
|
|
.then(...arguments) },
|
2020-11-24 05:48:54 +03:00
|
|
|
|
2020-11-17 21:35:04 +03:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Promise.iter([ .. ])
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
// Promise.iter([ .. ], handler)
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// handler(e)
|
2020-11-16 02:38:19 +03:00
|
|
|
// -> [value]
|
|
|
|
|
// -> []
|
|
|
|
|
//
|
2020-11-17 21:35:04 +03:00
|
|
|
//
|
|
|
|
|
// NOTE: element index is unknowable untill the full list is expanded
|
|
|
|
|
// 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...
|
2020-11-18 01:02:00 +03:00
|
|
|
//
|
|
|
|
|
//
|
2020-11-18 06:09:43 +03:00
|
|
|
// Spectial cases usefull for extending this constructor...
|
|
|
|
|
//
|
2020-11-18 01:02:00 +03:00
|
|
|
// Clone the iterator...
|
|
|
|
|
// Promise.iter([ .. ], false)
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
// Create a rejected iterator...
|
|
|
|
|
// Promise.iter(false)
|
|
|
|
|
// -> iterable-promise
|
|
|
|
|
//
|
|
|
|
|
//
|
2020-11-17 21:35:04 +03:00
|
|
|
__new__: function(_, list, handler){
|
2020-11-17 16:12:13 +03:00
|
|
|
var promise
|
|
|
|
|
|
2020-11-10 01:58:51 +03:00
|
|
|
// instance...
|
2020-11-24 05:48:54 +03:00
|
|
|
var obj = Reflect.construct(
|
|
|
|
|
IterablePromise.__proto__,
|
|
|
|
|
[function(resolve, reject){
|
2020-11-10 01:58:51 +03:00
|
|
|
// NOTE: this is here for Promise compatibilty...
|
|
|
|
|
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} }],
|
2020-11-10 01:58:51 +03:00
|
|
|
IterablePromise)
|
|
|
|
|
|
2020-11-17 16:12:13 +03:00
|
|
|
if(promise){
|
|
|
|
|
// apply handler(..) to the list...
|
2020-11-18 01:02:00 +03:00
|
|
|
//
|
2020-11-17 21:35:04 +03:00
|
|
|
// NOTE: the top level promises are not wrapped in arrays...
|
|
|
|
|
list =
|
2020-11-18 01:02:00 +03:00
|
|
|
// apply the handler...
|
2020-11-17 16:12:13 +03:00
|
|
|
handler ?
|
|
|
|
|
list.map(function(block){
|
|
|
|
|
return (block instanceof Array ?
|
|
|
|
|
block
|
|
|
|
|
: [block])
|
|
|
|
|
.map(function(e){
|
|
|
|
|
// NOTE: we are counting actual expanded
|
|
|
|
|
// values and not the "blocks"...
|
|
|
|
|
return (e && e.then && e.catch) ?
|
|
|
|
|
// promise...
|
|
|
|
|
e.then(function(v){
|
2020-11-17 21:35:04 +03:00
|
|
|
return handler(v) })
|
2020-11-17 16:12:13 +03:00
|
|
|
// basic value...
|
2020-11-17 21:35:04 +03:00
|
|
|
: handler(e) }) })
|
2020-11-17 16:12:13 +03:00
|
|
|
.flat()
|
2020-11-18 01:02:00 +03:00
|
|
|
// normal constructor...
|
2020-11-17 21:35:04 +03:00
|
|
|
: handler === undefined ?
|
|
|
|
|
list.map(function(e){
|
2020-11-17 16:12:13 +03:00
|
|
|
return e instanceof Promise ?
|
|
|
|
|
e
|
|
|
|
|
: [e] })
|
2020-11-18 01:02:00 +03:00
|
|
|
// clone...
|
2020-11-17 21:35:04 +03:00
|
|
|
: list.slice()
|
|
|
|
|
|
|
|
|
|
Object.defineProperty(obj, '__list', {
|
|
|
|
|
value: list,
|
|
|
|
|
enumerable: false,
|
|
|
|
|
})
|
2020-11-17 16:12:13 +03:00
|
|
|
|
|
|
|
|
// handle promise state...
|
|
|
|
|
Promise.all(list)
|
|
|
|
|
.then(function(res){
|
|
|
|
|
promise.resolve(res.flat()) })
|
|
|
|
|
.catch(promise.reject) }
|
|
|
|
|
|
2020-11-10 01:58:51 +03:00
|
|
|
return obj },
|
2020-11-09 06:22:32 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-11-24 05:48:54 +03:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
var InteractivePromise =
|
|
|
|
|
module.InteractivePromise =
|
|
|
|
|
object.Constructor('InteractivePromise', Promise, {
|
|
|
|
|
__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 = []
|
|
|
|
|
var onmessage = function(func){
|
|
|
|
|
handlers.push(func) }
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
})
|
|
|
|
|
return obj },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
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
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
var PromiseMixin =
|
|
|
|
|
module.PromiseMixin =
|
|
|
|
|
object.Mixin('PromiseMixin', 'soft', {
|
|
|
|
|
iter: IterablePromise,
|
2020-11-24 05:48:54 +03:00
|
|
|
interactive: InteractivePromise,
|
|
|
|
|
cooperative: CooperativePromise,
|
2020-11-22 23:50:05 +03:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PromiseMixin(Promise)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-11-02 18:24:20 +03:00
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
|
* vim:set ts=4 sw=4 : */ return module })
|