Skip to content

Commit 3591e38

Browse files
docs: js_event_loop
1 parent 2e93a57 commit 3591e38

File tree

4 files changed

+90
-11
lines changed

4 files changed

+90
-11
lines changed

applications/system/js_app/examples/apps/Scripts/event_loop.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,26 @@
22
/// <reference types="../../../types/event_loop" />
33
let event_loop = require("event_loop");
44

5+
// print a string after 1337 milliseconds
6+
event_loop.subscribe(event_loop.timer("oneshot", 1337), function (_subscription) {
7+
print("Hi after 1337 ms");
8+
});
9+
10+
// count up to 5 with a delay of 100ms between increments
11+
event_loop.subscribe(event_loop.timer("periodic", 100), function (subscription, counter) {
12+
print("Counter two:", counter);
13+
if (counter === 5)
14+
subscription.cancel();
15+
return [counter + 1];
16+
}, 0);
17+
18+
// count up to 15 with a delay of 100ms between increments
19+
// and stop the program when the count reaches 15
520
event_loop.subscribe(event_loop.timer("periodic", 100), function (_subscription, event_loop, counter) {
6-
print("Counter:", counter);
21+
print("Counter one:", counter);
722
if (counter === 15)
823
event_loop.stop();
924
return [event_loop, counter + 1];
1025
}, event_loop, 0);
1126

12-
event_loop.subscribe(event_loop.timer("oneshot", 1337), function (_subscription) {
13-
print("Hi after 1337 ms");
14-
});
15-
1627
event_loop.run();

applications/system/js_app/modules/js_event_loop.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,26 @@
33
#include <expansion/expansion.h>
44
#include <furi/core/event_loop_i.h>
55

6+
/**
7+
* @brief Per-module instance control structure
8+
*/
69
typedef struct {
710
FuriEventLoop* loop;
811
} JsEventLoop;
912

13+
/**
14+
* @brief Context passed to the generic event callback
15+
*/
1016
typedef struct {
1117
struct mjs* mjs;
1218
mjs_val_t callback;
1319
size_t arity;
1420
mjs_val_t* arguments;
1521
} JsEventLoopCallbackContext;
1622

23+
/**
24+
* @brief Generic event callback, handles all events
25+
*/
1726
static void js_event_loop_callback(void* param) {
1827
JsEventLoopCallbackContext* context = (JsEventLoopCallbackContext*)param;
1928
mjs_val_t result;
@@ -25,7 +34,7 @@ static void js_event_loop_callback(void* param) {
2534
context->arity,
2635
context->arguments);
2736

28-
// save returned value until next call
37+
// save returned value as args for next call
2938
if(mjs_array_length(context->mjs, result) != context->arity - 1) return;
3039
for(size_t i = 0; i < context->arity - 1; i++) {
3140
mjs_disown(context->mjs, &context->arguments[i + 1]);
@@ -34,6 +43,9 @@ static void js_event_loop_callback(void* param) {
3443
}
3544
}
3645

46+
/**
47+
* @brief Subscribes a JavaScript function to an event
48+
*/
3749
static void js_event_loop_subscribe(struct mjs* mjs) {
3850
// get arguments
3951
if(mjs_nargs(mjs) < 2)
@@ -73,16 +85,26 @@ static void js_event_loop_subscribe(struct mjs* mjs) {
7385
}
7486
}
7587

88+
/**
89+
* @brief Runs the event loop until it is stopped
90+
*/
7691
static void js_event_loop_run(struct mjs* mjs) {
7792
JsEventLoop* module = mjs_get_ptr(mjs, mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0));
7893
furi_event_loop_run(module->loop);
7994
}
8095

96+
/**
97+
* @brief Stops a running event loop
98+
*/
8199
static void js_event_loop_stop(struct mjs* mjs) {
82100
JsEventLoop* module = mjs_get_ptr(mjs, mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0));
83101
furi_event_loop_stop(module->loop);
84102
}
85103

104+
/**
105+
* @brief Creates a timer event that can be subscribed to just like and other
106+
* event
107+
*/
86108
static void js_event_loop_timer(struct mjs* mjs) {
87109
// get arguments
88110
if(mjs_nargs(mjs) != 2) JS_ERROR_AND_RETURN(mjs, MJS_BAD_ARGS_ERROR, "requires 2 arguments");
@@ -111,6 +133,8 @@ static void js_event_loop_timer(struct mjs* mjs) {
111133
mjs_return(mjs, mjs_mk_foreign(mjs, contract));
112134
}
113135

136+
// TODO: memory freeing, subscription cancellation
137+
114138
static void* js_event_loop_create(struct mjs* mjs, mjs_val_t* object) {
115139
mjs_val_t event_loop_obj = mjs_mk_object(mjs);
116140
JsEventLoop* module = malloc(sizeof(JsEventLoop));

applications/system/js_app/modules/js_event_loop.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,26 @@ typedef enum {
1010
JsEventLoopObjectTypeStream,
1111
} JsEventLoopObjectType;
1212

13+
/**
14+
* @brief Adapter for other JS modules that wish to integrate with the event
15+
* loop
16+
*
17+
* If another module wishes to integrate with `js_event_loop`, it needs to
18+
* implement a function that returns an mJS foreign pointer to an instance of
19+
* this structure. This value is then read by `event_loop`'s `subscribe`
20+
* function.
21+
*/
1322
typedef struct {
1423
JsEventLoopObjectType object_type;
1524
FuriEventLoopObject* object;
1625
union {
17-
FuriEventLoopEvent event;
26+
FuriEventLoopEvent
27+
event; //<! Event bitfield. Valid for all `object_type`s except `JsEventLoopObjectTypeTimer`
1828
struct {
19-
FuriEventLoopTimerType timer_type;
20-
uint32_t interval_ticks;
29+
FuriEventLoopTimerType
30+
timer_type; //<! Timer type (periodic or oneshot). Only valid for `JsEventLoopObjectTypeTimer`
31+
uint32_t
32+
interval_ticks; //<! Timer interval in ticks. Only valid for `JsEventLoopObjectTypeTimer`
2133
};
2234
};
2335
} JsEventLoopContract;
Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,47 @@
11
type Lit = undefined | null | {};
22

3+
/**
4+
* Subscription control interface
5+
*/
36
export interface Subscription {
7+
/**
8+
* Cancels the subscription, preventing any future events managed by the
9+
* subscription from firing
10+
*/
411
cancel(): void;
512
}
613

14+
/**
15+
* Opaque event source identifier
16+
*/
717
export type Contract = symbol;
818

9-
export type Callback<Args extends Lit[]> = (subscription: Subscription, ...args: Args) => Args | void;
19+
/**
20+
* A callback can be assigned to an event loop to listen to an event. It may
21+
* return an array with values that will be passed to it as arguments the next
22+
* time that it is called.
23+
*/
24+
export type Callback<Args extends Lit[]> = (subscription: Subscription, ...args: Args) => Args | undefined | void;
1025

26+
/**
27+
* Subscribes a callback to an event
28+
* @param contract Event identifier
29+
* @param callback Function to call when the event is triggered
30+
* @param args Initial arguments passed to the callback
31+
*/
1132
export function subscribe<Args extends Lit[]>(contract: Contract, callback: Callback<Args>, ...args: Args): Subscription;
12-
export function run(): void;
33+
/**
34+
* Runs the event loop until it is stopped (potentially never)
35+
*/
36+
export function run(): void | never;
37+
/**
38+
* Stops the event loop
39+
*/
1340
export function stop(): void;
1441

42+
/**
43+
* Creates a timer event that can be subscribed to just like any other event
44+
* @param mode Either `"oneshot"` or `"periodic"`
45+
* @param interval Timer interval in milliseconds
46+
*/
1547
export function timer(mode: "oneshot" | "periodic", interval: number): Contract;

0 commit comments

Comments
 (0)