From 8d401707c015fa605c13af850640c0d40a0f98f3 Mon Sep 17 00:00:00 2001 From: Graham Fenner Date: Wed, 24 Apr 2024 16:32:35 +0100 Subject: [PATCH] promise rework #2454 --- src/jswrap_promise.c | 612 +++++++++++++++++++++++++++---------------- src/jswrap_promise.h | 2 + 2 files changed, 381 insertions(+), 233 deletions(-) diff --git a/src/jswrap_promise.c b/src/jswrap_promise.c index 9429796db..bca3d1fa4 100644 --- a/src/jswrap_promise.c +++ b/src/jswrap_promise.c @@ -12,8 +12,10 @@ * * ES6 Promise implementation * ---------------------------------------------------------------------------- + * See https://github.com/espruino/Espruino/pull/2454 for better understanding. */ + #include "jsutils.h" #if ESPR_NO_PROMISES!=1 @@ -27,8 +29,16 @@ #define JS_PROMISE_CATCH_NAME JS_HIDDEN_CHAR_STR"cat" #define JS_PROMISE_REMAINING_NAME JS_HIDDEN_CHAR_STR"left" #define JS_PROMISE_RESULT_NAME JS_HIDDEN_CHAR_STR"res" -#define JS_PROMISE_RESOLVED_NAME "resolved" +#define JS_PROMISE_ISRESOLVED_NAME JS_HIDDEN_CHAR_STR"resolved" +#define JS_PROMISE_PROM_NAME JS_HIDDEN_CHAR_STR"prom" +#define JS_PROMISE_VALUE_NAME JS_HIDDEN_CHAR_STR"value" +#define JS_PROMISE_STATE_NAME JS_HIDDEN_CHAR_STR"state" +enum JS_PROMISE_STATE_ENUM { + JS_PROMISE_STATE_PENDING, + JS_PROMISE_STATE_REJECTED, + JS_PROMISE_STATE_FULFILLED +}; /*JSON{ "type" : "class", @@ -39,9 +49,8 @@ This is the built-in class for ES6 Promises */ -void _jswrap_promise_queueresolve(JsVar *promise, JsVar *data); -void _jswrap_promise_queuereject(JsVar *promise, JsVar *data); -void _jswrap_promise_add(JsVar *parent, JsVar *callback, bool resolve); +void _jswrap_promise_resolve(JsVar *prombox, JsVar *data); +void _jswrap_promise_reject(JsVar *prombox, JsVar *data); bool _jswrap_promise_is_promise(JsVar *promise) { JsVar *constr = jspGetConstructor(promise); @@ -50,92 +59,72 @@ bool _jswrap_promise_is_promise(JsVar *promise) { return isPromise; } - -void _jswrap_promise_resolve_or_reject(JsVar *promise, JsVar *data, JsVar *fn) { - // remove any existing handlers since we already have them in `fn` - // If while we're iterating below a function re-adds to the chain then - // we can execute that later - // https://github.com/espruino/Espruino/issues/894#issuecomment-402553934 - jsvObjectRemoveChild(promise, JS_PROMISE_THEN_NAME); // remove 'resolve' and 'reject' handlers - jsvObjectRemoveChild(promise, JS_PROMISE_CATCH_NAME); // remove 'resolve' and 'reject' handlers - JsVar *chainedPromise = jsvObjectGetChildIfExists(promise, "chain"); - jsvObjectRemoveChild(promise, "chain"); // unlink chain - // execute handlers from `fn` - JsVar *result = 0; - if (jsvIsArray(fn)) { - JsvObjectIterator it; - jsvObjectIteratorNew(&it, fn); - bool first = true; - while (jsvObjectIteratorHasValue(&it)) { - JsVar *f = jsvObjectIteratorGetValue(&it); - JsVar *v = jspExecuteFunction(f, promise, 1, &data); - if (first) { - first = false; - result = v; - } else jsvUnLock(v); - jsvUnLock(f); - jsvObjectIteratorNext(&it); - } - jsvObjectIteratorFree(&it); - } else if (fn) { - result = jspExecuteFunction(fn, promise, 1, &data); - } - - JsVar *exception = jspGetException(); - if (exception) { - _jswrap_promise_queuereject(chainedPromise, exception); - jsvUnLock3(exception, result, chainedPromise); +// A single reaction chain - this is recursive until a promise is returned or chain ends. data can be undefined/0 +void _jswrap_promise_reaction_call(JsVar *promise, JsVar *reaction, JsVar *data, JsVar *isThen) { + JsVar *exceptionName = jsvFindChildFromString(execInfo.hiddenRoot, JSPARSE_EXCEPTION_VAR); + if (exceptionName) { + jsvUnLock(exceptionName); return; } - - if (chainedPromise) { - if (_jswrap_promise_is_promise(result)) { - // if we were given a promise, loop its 'then' in here - JsVar *fnres = jsvNewNativeFunction((void (*)(void))_jswrap_promise_queueresolve, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<