ensure that calling 'then' on a promise that has already completed still works

This commit is contained in:
Gordon Williams 2016-11-18 10:55:14 +00:00
parent bbddab9286
commit 0f4da0a64f
2 changed files with 33 additions and 7 deletions

View File

@ -34,7 +34,7 @@ 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, const char *name);
void _jswrap_promise_add(JsVar *parent, JsVar *callback, bool resolve);
bool _jswrap_promise_is_promise(JsVar *promise) {
JsVar *constr = jspGetConstructor(promise);
@ -43,6 +43,7 @@ bool _jswrap_promise_is_promise(JsVar *promise) {
return isPromise;
}
void _jswrap_promise_resolve_or_reject(JsVar *promise, JsVar *data, JsVar *fn) {
JsVar *result = 0;
if (jsvIsArray(fn)) {
@ -81,7 +82,7 @@ void _jswrap_promise_resolve_or_reject(JsVar *promise, JsVar *data, JsVar *fn) {
JsVar *fn = jsvNewNativeFunction((void (*)(void))_jswrap_promise_queueresolve, JSWAT_VOID|JSWAT_THIS_ARG|(JSWAT_JSVAR<<JSWAT_BITS));
if (fn) {
jsvObjectSetChild(fn, JSPARSE_FUNCTION_THIS_NAME, chainedPromise);
_jswrap_promise_add(result, fn, JS_PROMISE_THEN_NAME);
_jswrap_promise_add(result, fn, true);
jsvUnLock(fn);
}
} else {
@ -112,7 +113,9 @@ void _jswrap_promise_resolve_or_reject_chain(JsVar *promise, JsVar *data, bool r
_jswrap_promise_resolve_or_reject(promise, data, fn);
jsvUnLock(fn);
} else {
if (!resolve)
if (resolve)
jsvObjectSetChild(promise, "resolved", data);
else
jsExceptionHere(JSET_ERROR, "Unhandled promise rejection: %v", data);
}
}
@ -308,11 +311,27 @@ JsVar *jswrap_promise_reject(JsVar *data) {
return promise;
}
void _jswrap_promise_add(JsVar *parent, JsVar *callback, const char *name) {
void _jswrap_promise_add(JsVar *parent, JsVar *callback, bool resolve) {
if (!jsvIsFunction(callback)) {
jsExceptionHere(JSET_TYPEERROR, "Callback must be a function, got %t", callback);
return;
}
if (resolve) {
// Check to see if promise has already been resolved
/* Note: we use jsvFindChildFromString not ObjectGetChild so we get the name.
* If we didn't then we wouldn't know if it was resolved, but with undefined */
JsVar *resolved = jsvFindChildFromString(parent, "resolved", 0);
if (resolved) {
resolved = jsvSkipNameAndUnLock(resolved);
// If so, queue a resolve event
jsiQueueEvents(0, callback, &resolved, 1);
jsvUnLock(resolved);
return;
}
}
const char *name = resolve ? JS_PROMISE_THEN_NAME : JS_PROMISE_CATCH_NAME;
JsVar *c = jsvObjectGetChild(parent, name, 0);
if (!c) {
jsvObjectSetChild(parent, name, callback);
@ -352,9 +371,9 @@ static JsVar *jswrap_promise_get_chained_promise(JsVar *parent) {
}
*/
JsVar *jswrap_promise_then(JsVar *parent, JsVar *onFulfilled, JsVar *onRejected) {
_jswrap_promise_add(parent, onFulfilled, JS_PROMISE_THEN_NAME);
_jswrap_promise_add(parent, onFulfilled, true);
if (onRejected)
_jswrap_promise_add(parent, onRejected, JS_PROMISE_CATCH_NAME);
_jswrap_promise_add(parent, onRejected, false);
return jswrap_promise_get_chained_promise(parent);
}
@ -371,6 +390,6 @@ JsVar *jswrap_promise_then(JsVar *parent, JsVar *onFulfilled, JsVar *onRejected)
}
*/
JsVar *jswrap_promise_catch(JsVar *parent, JsVar *onRejected) {
_jswrap_promise_add(parent, onRejected, JS_PROMISE_CATCH_NAME);
_jswrap_promise_add(parent, onRejected, false);
return jswrap_promise_get_chained_promise(parent);
}

7
tests/test_promise6.js Normal file
View File

@ -0,0 +1,7 @@
// Test using `then` on an already-resolved promise
var p = new Promise( function(resolve,reject) { resolve(42); });
setTimeout(function() {
p.then( function(value) {
result = value==42;
});
},1);