I read the Promise/A+ specification and it says under 2.2.4:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code
But in Firefox (I tested 38.2.1 ESR and 40.0.3) the following script executes the onFulfilled method synchronously:
var p = Promise.resolve("Second");
p.then(alert);
alert("First");
(It does not seem to run using alerts here, it can also be tried here: http://jsbin.com/yovemaweye/1/edit?js,output)
It works as expected in other browsers or when using the ES6Promise-Polyfill.
Did I miss something here? I always though that one of the points of the then-method is to ensure asynchronous execution.
Edit:
It works when using console.log, see answer by Benjamin Gruenbaum:
function output(sMessage) {
console.log(sMessage);
}
var p = Promise.resolve("Second");
p.then(output);
output("First");
As he points out in the comments, this also happens when using synchronous requests, which is exactly why it happens in your test scenario. I created a minimal example of what happens in our Tests:
function request(bAsync) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
resolve(xhr.responseText);
}
});
xhr.open("GET", "https://sapui5.hana.ondemand.com/sdk/resources/sap-ui-core.js", !!bAsync);
xhr.send();
});
}
function output(sMessage, bError) {
var oMessage = document.createElement("div");
if (bError) {
oMessage.style.color = "red";
}
oMessage.appendChild(document.createTextNode(sMessage));
document.body.appendChild(oMessage);
}
var sSyncData = null;
var sAsyncData = null;
request(true).then(function(sData) {
sAsyncData = sData;
output("Async data received");
});
request(false).then(function(sData) {
sSyncData = sData;
output("Sync data received");
});
// Tests
if (sSyncData === null) {
output("Sync data as expected");
} else {
output("Unexpected sync data", true);
}
if (sAsyncData === null) {
output("Async data as expected");
} else {
output("Unexpected async data", true);
}
In Firefox this leads to:
A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed.
Return valuereturns a value, the promise returned by then gets resolved with the returned value as its value. doesn't return anything, the promise returned by then gets resolved with an undefined value. throws an error, the promise returned by then gets rejected with the thrown error as its value.
Fulfillment. The returned promise is fulfilled with an array containing all the fulfilled values (including non-promise values) in the iterable passed as the argument. If an empty iterable is passed, then the promise returned by this method is fulfilled synchronously.
alert
When you use alert
here it blocks and all bets are off - the page has frozen, execution halted and things are at "platform level".
It might be considered a bug, and it's certainly not what I would expect - but at the core this is about the incompatibility between alert
and JavaScript task/microtask semantics.
If you change that alert to a console.log
or appending to document.innerHTML
you'd get the result you expect.
var alert = function(arg) { // no longer a magical and blocking operation
document.body.innerHTML += "<br/>" + arg;
}
// this code outputs "First, Second, Third" in all the browsers.
setTimeout(alert.bind(null, "Third"), 0);
var p = Promise.resolve("Second");
p.then(alert);
alert("First");
From what I can tell, this is actually permitted optional behavior:
Optionally, pause while waiting for the user to acknowledge the message.
(Emphasis mine)
alert
.then
is run as a microtask, so "Second" gets queued and precedes the alert
.Second
alert gets run.First
alert gets run.Confusing, but allowed from what I can tell.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With