Hi,
Here's a question about promise chaining. C being what it is, it's harder
to define a series like p.then().then() than it is in JS. And I can't find
any good example in EFL code.
Let's consider an abstract chain that I'll define as "download, unzip,
show". (eg. download a zip file, unzip it and set the file on an image
object).
All of these are async, we don't care about the result of show. I don't
understand how the chain should be written:
main_func(url) {
Efl_Future *f1 = download_url(url);
Efl_Future *f2 = efl_future_then(f1, _down_cb, ..., NULL);
efl_future_then(f2, _unzip_cb, ..., image);
}
// So far I think we're ok
void _down_cb(null, event) {
Efl_Promise *next = event->info->next;
Efl_Future *f3 = _async_unzip(event->infodata);
efl_future_then(f3, _unzip_cb2, .., next);
}
// I'm already getting confused with next, f2 and f3. Something is odd.
void _unzip_cb(data, event) {
Image *image = data;
show(image);
}
void _unzip_cb2(data, event) {
Efl_Promise *p2 = data;
efl_promise_value_set(p3, NULL, NULL); // --> calls unzip_cb
}
So my questions:
1. Is this above code correct?
2. (How) are we ensuring that the chain eventually resolves?
3. Couldn't this be made in a simpler way? unzip_cb2 is really just an
automatic resolve of another promise/future.
In unzip_cb, if I ignore "next", f2 will never come to completion.
So here's a proposal:
1. Get rid of info->next. Instead:
Efl_Promise *p2 = efl_promise_next(event->object);
That way, EFL knows that the developer has not ignored the promise. The
developer must then call efl_promise_value/error_set at some point, sync or
async.
If the callback doesn't call efl_promise_next, EFL knows that the "next"
promise will never come to completion, ever. In that case, EFL
automatically calls efl_promise_value_set(next, NULL, NULL) to keep walking
the chain. (Another idea is to instead cancel it).
According to [ https://promisesaplus.com/ ], if onResolve is not a function
(success_cb is NULL), then p2 is fullfilled with the same value as p1. So I
guess we would just call _unzip_cb directly in the above example.
Without efl_promise_next() the callback must always handle "next". Failure
to do so will break all chains, and leak. The value of "next" can be set
inside _efl_loop_future_success.
2. (raster's idea) Get rid of event->info
Similarly, use efl_promise_value_get() instead of event->info->value. That
way we can implement stealing with efl_promise_value_steal(&val, &freefunc)
and keep the symmetry with value_get().
In that case, efl_ref(promise) can be used to keep a value alive for as
long as you want. It's just a storage object. [if not eo, then promise_ref,
whatever]
Any weak ref by efl_promise_use probably needs to be reset to NULL after
the success/fail callback.
3. multiple then, race, all order of operations
WIth steal, and any kind of cleanup callback, the order of operations is
crucial. Is it well defined? Example:
f1 = function();
efl_future_then(f1, _then1, ...);
efl_future_then(f1, _then2, ...);
efl_future_then(f1, _then3, ...);
// then1, then2, then3 MUST be called in this order (I think)
f2 = function();
efl_future_then(efl_future_all(f1, f2), _then4);
efl_future_then(f2, _then5, ...);
// will it be _then4, _then5 or _then5, _then4?
// same question for race
// I believe the implementation is fine here, but not many docs/tests/uses
:(
Thoughts?
Is the above OK or completely off track?
PS: Sorry this mail became longer than I thought at first :-/
--
Jean-Philippe André
------------------------------------------------------------------------------
_______________________________________________
enlightenment-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel