Skip to content

Commit 3c5bac9

Browse files
authored
Implement emscripten_promise_any in promise.h (#19153)
Similar to `emscripten_promise_all` and `emscripten_promise_all_settled`, this function propagates the first of its input promises to be fulfilled or is rejected with a list of reasons if its inputs are rejected.
1 parent 2bf3000 commit 3c5bac9

File tree

5 files changed

+167
-3
lines changed

5 files changed

+167
-3
lines changed

src/library_promise.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,29 @@ mergeInto(LibraryManager.library, {
215215
#endif
216216
return id;
217217
},
218+
219+
emscripten_promise_any__deps: ['$promiseMap', '$idsToPromises'],
220+
emscripten_promise_any: function(idBuf, errorBuf, size) {
221+
var promises = idsToPromises(idBuf, size);
222+
#if RUNTIME_DEBUG
223+
dbg('emscripten_promise_any: ' + promises);
224+
#endif
225+
#if ASSERTIONS
226+
assert(typeof Promise.any !== 'undefined', "Promise.any does not exist");
227+
#endif
228+
var id = promiseMap.allocate({
229+
promise: Promise.any(promises).catch((err) => {
230+
if (errorBuf) {
231+
for (var i = 0; i < size; i++) {
232+
{{{ makeSetValue('errorBuf', `i*${POINTER_SIZE}`, 'err.errors[i]', '*') }}};
233+
}
234+
}
235+
throw errorBuf;
236+
})
237+
});
238+
#if RUNTIME_DEBUG
239+
dbg('create: ' + id);
240+
#endif
241+
return id;
242+
}
218243
});

src/library_sigs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ sigs = {
537537
emscripten_print_double__sig: 'idpi',
538538
emscripten_promise_all__sig: 'pppp',
539539
emscripten_promise_all_settled__sig: 'pppp',
540+
emscripten_promise_any__sig: 'pppp',
540541
emscripten_promise_create__sig: 'p',
541542
emscripten_promise_destroy__sig: 'vp',
542543
emscripten_promise_resolve__sig: 'vpip',

system/include/emscripten/promise.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ typedef struct em_settled_result_t {
116116
__attribute__((warn_unused_result)) em_promise_t emscripten_promise_all_settled(
117117
em_promise_t* promises, em_settled_result_t* results, size_t num_promises);
118118

119+
// Call Promise.any to create and return a new promise that is fulfilled once
120+
// any of the `num_promises` input promises passed in `promises` has been
121+
// fulfilled or is rejected once all of the input promises have been rejected.
122+
// If the returned promise is fulfilled, it will be fulfilled with the same
123+
// value as the first fulfilled input promise. Otherwise, if the returned
124+
// promise is rejected, the rejection reasons for each input promise will be
125+
// written to the `errors` buffer if it is non-null. The rejection reason for
126+
// the returned promise will also be the address of the `errors` buffer.
127+
__attribute__((warn_unused_result)) em_promise_t emscripten_promise_any(
128+
em_promise_t* promises, void** errors, size_t num_promises);
129+
119130
#ifdef __cplusplus
120131
}
121132
#endif

test/core/test_promise.c

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,13 +334,131 @@ test_all_settled(void** result, void* data, void* value) {
334334
emscripten_promise_destroy(null_in[2]);
335335

336336
em_promise_t to_finish[3] = {empty_checked, full_checked, null_checked};
337-
em_promise_t finish_test_all = emscripten_promise_all(to_finish, NULL, 3);
337+
em_promise_t finish_test_all_settled =
338+
emscripten_promise_all(to_finish, NULL, 3);
338339

339340
emscripten_promise_destroy(empty_checked);
340341
emscripten_promise_destroy(full_checked);
341342
emscripten_promise_destroy(null_checked);
342343

343-
*result = finish_test_all;
344+
*result = finish_test_all_settled;
345+
return EM_PROMISE_MATCH_RELEASE;
346+
}
347+
348+
typedef struct promise_any_state {
349+
size_t size;
350+
em_promise_t in[3];
351+
void* expected;
352+
void* err[3];
353+
void* expected_err[3];
354+
} promise_any_state;
355+
356+
static em_promise_result_t
357+
check_promise_any_result(void** result, void* data, void* value) {
358+
promise_any_state* state = (promise_any_state*)data;
359+
emscripten_console_logf("promise_any result: %ld", (uintptr_t)value);
360+
assert(value == state->expected);
361+
free(state);
362+
return EM_PROMISE_FULFILL;
363+
}
364+
365+
static em_promise_result_t
366+
check_promise_any_err(void** result, void* data, void* value) {
367+
promise_any_state* state = (promise_any_state*)data;
368+
assert(value == state->err);
369+
emscripten_console_log("promise_any reasons:");
370+
for (size_t i = 0; i < state->size; ++i) {
371+
emscripten_console_logf("%ld", (uintptr_t)state->err[i]);
372+
assert(state->err[i] == state->expected_err[i]);
373+
}
374+
free(state);
375+
return EM_PROMISE_FULFILL;
376+
}
377+
378+
static em_promise_result_t test_any(void** result, void* data, void* value) {
379+
emscripten_console_log("test_any");
380+
assert(data == (void*)6);
381+
382+
// No input should be handled ok and be rejected.
383+
promise_any_state* state = malloc(sizeof(promise_any_state));
384+
*state = (promise_any_state){
385+
.size = 0, .in = {}, .expected = NULL, .err = {}, .expected_err = {}};
386+
em_promise_t empty =
387+
emscripten_promise_any(state->in, state->err, state->size);
388+
em_promise_t empty_checked =
389+
emscripten_promise_then(empty, fail, check_promise_any_err, state);
390+
emscripten_promise_destroy(empty);
391+
392+
// The first fulfilled promise should be propagated.
393+
state = malloc(sizeof(promise_any_state));
394+
*state = (promise_any_state){.size = 3,
395+
.in = {emscripten_promise_create(),
396+
emscripten_promise_create(),
397+
emscripten_promise_create()},
398+
.expected = (void*)42,
399+
.err = {},
400+
.expected_err = {}};
401+
em_promise_t full =
402+
emscripten_promise_any(state->in, state->err, state->size);
403+
em_promise_t full_checked =
404+
emscripten_promise_then(full, check_promise_any_result, fail, state);
405+
emscripten_promise_destroy(full);
406+
emscripten_promise_resolve(state->in[0], EM_PROMISE_REJECT, (void*)41);
407+
emscripten_promise_resolve(state->in[1], EM_PROMISE_FULFILL, (void*)42);
408+
emscripten_promise_resolve(state->in[2], EM_PROMISE_FULFILL, (void*)43);
409+
emscripten_promise_destroy(state->in[0]);
410+
emscripten_promise_destroy(state->in[1]);
411+
emscripten_promise_destroy(state->in[2]);
412+
413+
// If all promises are rejected, the result will be rejected with the reasons.
414+
state = malloc(sizeof(promise_any_state));
415+
*state =
416+
(promise_any_state){.size = 3,
417+
.in = {emscripten_promise_create(),
418+
emscripten_promise_create(),
419+
emscripten_promise_create()},
420+
.expected = NULL,
421+
.err = {},
422+
.expected_err = {(void*)42, (void*)43, (void*)44}};
423+
em_promise_t rejected =
424+
emscripten_promise_any(state->in, state->err, state->size);
425+
em_promise_t rejected_checked =
426+
emscripten_promise_then(rejected, fail, check_promise_any_err, state);
427+
emscripten_promise_destroy(rejected);
428+
emscripten_promise_resolve(state->in[0], EM_PROMISE_REJECT, (void*)42);
429+
emscripten_promise_resolve(state->in[1], EM_PROMISE_REJECT, (void*)43);
430+
emscripten_promise_resolve(state->in[2], EM_PROMISE_REJECT, (void*)44);
431+
emscripten_promise_destroy(state->in[0]);
432+
emscripten_promise_destroy(state->in[1]);
433+
emscripten_promise_destroy(state->in[2]);
434+
435+
// Same, but now the error reason buffer is null.
436+
em_promise_t null_in[3] = {
437+
emscripten_promise_create(),
438+
emscripten_promise_create(),
439+
emscripten_promise_create(),
440+
};
441+
em_promise_t null = emscripten_promise_any(null_in, NULL, 3);
442+
em_promise_t null_checked =
443+
emscripten_promise_then(null, fail, check_null, NULL);
444+
emscripten_promise_destroy(null);
445+
emscripten_promise_resolve(null_in[0], EM_PROMISE_REJECT, (void*)42);
446+
emscripten_promise_resolve(null_in[1], EM_PROMISE_REJECT, (void*)43);
447+
emscripten_promise_resolve(null_in[2], EM_PROMISE_REJECT, (void*)44);
448+
emscripten_promise_destroy(null_in[0]);
449+
emscripten_promise_destroy(null_in[1]);
450+
emscripten_promise_destroy(null_in[2]);
451+
452+
em_promise_t to_finish[4] = {
453+
empty_checked, full_checked, rejected_checked, null_checked};
454+
em_promise_t finish_test_any = emscripten_promise_all(to_finish, NULL, 4);
455+
456+
emscripten_promise_destroy(empty_checked);
457+
emscripten_promise_destroy(full_checked);
458+
emscripten_promise_destroy(rejected_checked);
459+
emscripten_promise_destroy(null_checked);
460+
461+
*result = finish_test_any;
344462
return EM_PROMISE_MATCH_RELEASE;
345463
}
346464

@@ -390,8 +508,9 @@ int main() {
390508
em_promise_t test4 = emscripten_promise_then(test3, test_all, fail, (void*)4);
391509
em_promise_t test5 =
392510
emscripten_promise_then(test4, test_all_settled, fail, (void*)5);
511+
em_promise_t test6 = emscripten_promise_then(test5, test_any, fail, (void*)6);
393512
em_promise_t assert_stack =
394-
emscripten_promise_then(test5, check_stack, fail, NULL);
513+
emscripten_promise_then(test6, check_stack, fail, NULL);
395514
em_promise_t end = emscripten_promise_then(assert_stack, finish, fail, NULL);
396515

397516
emscripten_promise_resolve(start, EM_PROMISE_FULFILL, NULL);
@@ -403,6 +522,7 @@ int main() {
403522
emscripten_promise_destroy(test3);
404523
emscripten_promise_destroy(test4);
405524
emscripten_promise_destroy(test5);
525+
emscripten_promise_destroy(test6);
406526
emscripten_promise_destroy(assert_stack);
407527
emscripten_promise_destroy(end);
408528

test/core/test_promise.out

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,11 @@ promise_all_settled results:
2121
fulfill 42
2222
reject 43
2323
fulfill 44
24+
test_any
25+
promise_any reasons:
26+
promise_any result: 42
27+
promise_any reasons:
28+
42
29+
43
30+
44
2431
finish

0 commit comments

Comments
 (0)