File: integration.md

package info (click to toggle)
libdex 1.1~alpha-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,380 kB
  • sloc: ansic: 15,259; python: 550; asm: 226; xml: 39; makefile: 15; sh: 6; javascript: 6
file content (85 lines) | stat: -rw-r--r-- 3,198 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Title: Integrating GAsyncResult

Historically if you wanted to do asynchronous work in GObject-based applications you would use [callback@Gio.AsyncReadyCallback] and [iface@Gio.AsyncResult].

There are two ways to integrate with this form of asynchronous API.
In one direction, you can consume this historical API and provide the result as a [class@Dex.Future].
In the other direction, you can provide this API in your application or library but implement it behind the scenes with [class@Dex.Future].

# Converting GAsyncResult to Futures

A typical case to integrate, at least initially, is to extract the result of a [iface@Gio.AsyncResult] and propagate it to a future.

One way to do that is with a [class@Dex.Promise] which will resolve or reject from your async callback.

```c
static void
my_callback (GObject      *object,
             GAsyncResult *result,
             gpointer      user_data)
{
  g_autoptr(DexPromise) promise = user_data;
  g_autoptr(GError) error = NULL;

  if (thing_finish (THING (object), result, &error))
    dex_promise_resolve_boolean (promise, TRUE);
  else
    dex_promise_reject (promise, g_steal_pointer (&error));
}

static DexFuture *
my_wrapper (Thing *thing)
{
  DexPromise *promise = dex_promise_new_cancellable ();

  thing_async (thing,
               dex_promise_get_cancellable (promise),
               my_callback,
               dex_ref (promise));

  return DEX_FUTURE (promise);
}
```

# Implementing AsyncResult with Futures

In some cases you may not want to "leak" into your API that you are using [class@Dex.Future].
For example, you may want to only expose a traditional GIO API or maybe even rewriting legacy code.

For these cases use [class@Dex.AsyncResult].
It is designed to feel familiar to those that have used [class@Gio.Task].

[ctor@Dex.AsyncResult.new] allows taking the typical `cancellable`, `callback`, and `user_data` parameters similar to [class@Gio.Task].

Then call [method@Dex.AsyncResult.await] providing the future that will resolve or reject with error.
One completed, the users provided `callback` will be executed within the active scheduler at time of creation.

From your finish function, call the appropriate propagate API such as [method@Dex.AsyncResult.propagate_int].

```c
void
thing_async (Thing               *thing,
             GCancellable        *cancellable,
             GAsyncReadyCallback  callback,
             gpointer             user_data)
{
  g_autoptr(DexAsyncResult) result = NULL;

  result = dex_async_result_new (thing, cancellable, callback, user_data);
  dex_async_result_await (result, dex_future_new_true ());
}

gboolean
thing_finish (Thing         *thing,
              GAsyncResult  *result,
              GError       **error)
{
  return dex_async_result_propagate_boolean (DEX_ASYNC_RESULT (result), error);
}
```

# Safety Notes

One thing that Libdex handles better than `GTask` is ensuring that your `user_data` is destroyed on the proper thread.
The design of [class@Dex.Block] was done in such a way that both the result and `user_data` are passed back to the owning thread at the same time.
This ensures that your `user_data` will never be finalized on the wrong thread.