Master de II. ULL. 1er cuatrimestre
Consider this file index.html
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Asynchronous Image Loading</title>
</head>
<body>
<div id="holder-div"></div>
<script type="text/javascript">
let image = new Image(100),
//url = "https://elementscpa.com/wp-content/uploads/2017/08/elementscpa-business-infinity-loop-tal-e1504182065499.png",
url = "infinity-loop.png",
container = document.getElementById("holder-div");
image.src = url; // I suppose the "load" process starts here
let waitFor = 0;
//let waitFor = 2000;
setTimeout(function(){
// The onload event occurs when an object has been loaded
// We only append it to the container when the load has finished
// If 1000 the handler is inserted in the event queue too late
// If an EventListener is added to an EventTarget while it is
// processing an event, that event does not trigger the listener.
image.addEventListener("load", function() {
console.trace();
container.appendChild(image)
});
}, waitFor);
</script>
<a href="http://www.infoq.com/presentations/javascript-concurrency-parallelism">Concurrency and Parallel Computing in JavaScript (Recorded at: StrangeLoop) by Stephan Herhut on Mar 05, 2014 </a>
</body>
</html>
Copy and serve this file index.html with:
1
http-server -p 9000 -o
with this line uncommented
1
let waitFor = 1000;
Can you see the infinite loop image?
Now comment the line where waitFor
is initialized and uncomment the other:
1
let waitFor = 0;
and run:
1
http-server -p 8000 -o
(Change the port to avoid cache problems)
What do you think it will happen? Can you explain it?
Where is it:
1
2
➜ race-condition git:(curso2021) ✗ ls ~/campus-virtual/2021/sytws2021/apuntes/tema2-async/event-loop/exercises/race-condition
index.html infinity-loop.png instructions.md not-race-example.js race-example.js
Folders:
1
2
~/campus-virtual/2021/sytws2021/apuntes/tema2-async/event-loop/exercises/race-condition
~/local/src/uai/uai2015/race-condition/index.html
This other example (using promises instead of callbacks) is taken from this blog:
Promises are a (relatively) new way to handle asynchronous programming in JS. For an introduction to Promises see this section:
File race-example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// https://medium.com/@ubershmekel/yes-there-are-race-conditions-in-javascript-ba044571a914
// An example race condition in JavaScript
// When you run this script using Node or in a browser, it
// does not print "Ended with 0", but a random number.
// Even though the functions running
// simply loop 100 iterations of adding and subtracting.
// The reason the end result is random is because the
// sleeps are of random duration and the time between the read
// of the variable causes the eventual write to be incorrect
// when `adder` and `subber` interleave.
// This problem is similar to:
// https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
let number = 0;
const times = 100;
function sleep() {
return new Promise(
(resolve) => setTimeout(resolve, Math.random() * 5));
}
async function adder() {
for (let i = 0; i < times; i++) {
await sleep();
let read = number;
read = read + 1;
await sleep(); // This task is interrupted here giving opportunity for 'subber' to take the processor
number = read; // But there is a chance 'read' is obsolete.
// It is a sort of "manual" co-routine race cndition: not atomic anymore
}
}
async function subber() {
for (let i = 0; i < times; i++) {
await sleep();
let read = number;
read = read - 1;
await sleep();
number = read;
}
}
async function main() {
console.log("Started with", number);
await Promise.all([
adder(),
subber(),
]);
/*
await adder().then(subber)
*/
console.log("Ended with", number);
}
main()
.then(() => console.log("All done"))
.catch((err) => console.error(err));
An example of race condition in JavaScript. When you run this script using Node or in a browser, it does not print “Ended with 0”, but a random number.
1
2
3
4
5
6
7
➜ race-condition git:(curso2021) ✗ node race-example.js
Started with 0
Ended with 3
All done
➜ race-condition git:(curso2021) ✗ node race-example.js
Started with 0
Ended with 20
Even though the functions running simply loop 100 iterations of adding and subtracting.
The reason the end result is random is because the
sleeps are of random duration and the time between the read
of the variable causes the eventual write to be incorrect
when adder
and subber
interleave.
This problem is similar to: