问题
(The first obstacle when posting this was deciding on a good title - hope I did OK with that.)
I'm slightly bewildered by how the native JavaScript Promise object behaves (tested in Chrome and Firefox in Windows 7), and whether or not they actually execute in parallel or not. I sought enlightenment here on SO, but found none as of yet.
Consider this block of code:
(function PromiseTester() {
var counter = 0;
new Promise(function(resolve) {
for(var i = 0; i < 500000000; i++)
counter = i;
resolve();
}).then(function() {console.log('a: ' + counter)});
new Promise(function(resolve) {
resolve();
}).then(function() {console.log('b: ' + counter)});
console.log('When is this?');
})();
How can I explain the following output to the console?
When is this?
a: 499999999
b: 499999999
It would seem that although creating the Promises is not a blocking operation in itself, the blocking loop in the first one effectively hinders the second one from resolving first.
I also tried putting the Promiseobjects in an array and test them with Promise.race(). It seems that code in the then() method of the race() Promise does not execute until the loop in the first Promise is finished.
Maybe this is all sober and dandy, but I don't quite get what to make of it all. Shouldn't the Promise objects execute and resolve in parallel?
I'd be much obliged for any attempts to clarify the situation, and how to use Promise properly for parallel execution.
(Please not that this question is not about Promise.resolve(), Promise.all() etc. but about the parallel - or possibly not parallel - nature of a JavaScript Promise.)
EDIT: In some of my comments, I said I have the same issue as described above even when replacing the loop with something asynchronous. This was wrong, and just to avoid any ambiguity, here's an example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Promise Tester</title>
</head>
<body>
<p>Check the console!</p>
<script type="text/javascript">
(function Main() {
var urls = [
'Pages/SlowPage.aspx',
'Pages/Page1.html',
'Pages/SlowerPage.aspx',
'Pages/Page2.html'
];
var promises = [];
for (var i = 0; i < urls.length; i++) {
(function AddPromise(url) {
promises.push(
new Promise(function (resolve) {
var request = new XMLHttpRequest();
request.onload = function () {
resolve(url);
};
request.open('GET', url, true);
request.send();
})
.then(function (result) {
console.log('Resolved ' + url + '.');
})
);
})(urls[i])
}
Promise
.race(promises)
.then(function () {
console.log('First promise resolved.');
});
Promise
.all(promises)
.then(function () {
console.log('All promises resolved.');
});
})();
</script>
</body>
</html>
The html pages are just that - plain and simple HTML pages, but in the aspx pages i put some server side Thread.Sleep() code to make them "slow". While perhaps not one-hundred-percent-absolutely-bulletproof, it should provide a sufficient solution for testing in this context, and the output to the console is as follows:
Resolved Pages/Page1.html.
Resolved Pages/Page2.html.
First promise resolved.
Resolved Pages/SlowPage.aspx.
Resolved Pages/SlowerPage.aspx.
All promises resolved.
In my original question, I thought it was confusing that the phrase "When is this?" was logged before any of the Promise objects resolved. Likewise, I think it's somewhat unexpected that the html pages consistently (always?) resolves before Promise.race realizes that at least one Promise was resolved. If anyone would care to further elaborate on that I'd be interested to hear, but if not I'm satisfied with the conclusion that "that's just how it is", for now.
EDIT: I really mean "concurrent" rather than "parallel" in all of the above.
回答1:
Ah, the confusion between an event loop and multithreading...
When you instantiate your first promise, the underlying implementation is such that after the creation, JavaScript hands over control back to the next instruction, which is to start the loop. This instruction (if you prefer, the function IETF in the promise) starts running, and does not stop until the loop has run its full course. At no point is there a way for the event loop to notice that your loop is "partially done" but that it's okay to go and slot in a couple of operations until the next iteration.
When the loop is over, the promise is marked as completed, and the event loop decides to pick the next in order - your second promise!
If you'd like to do it another way without invoking webworkers or switching language, at a huge cost of performance, you could process.nextTick() (or, since you're in a browser, setTimeout(function() {}, 0)) every iteration of your loop in order to see that what I'm saying is correct. You will then see promise #2 being completed due to every iteration of the loop being "handed back" to the event loop.
In reality, where you are expecting JS to be multithreaded, it is just event-driven. Difference in conception with huge implications.
来源:https://stackoverflow.com/questions/28856509/how-native-javascript-promise-handles-blocking-code