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
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to