I'm reading YDKJS and early on we are talking about the difference between Async, Parallel and Concurrent Code.
I have a simple Async example:
let output = 0;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
output = 1;
});
foo( () => {
output = 2
});
setTimeout(() => {
// This Async code should have 2 different outputs
output;
}, 2000);
The above code can have the 2 answers based on the Math.random timer and the mutable output:
However, I'd like to add a bit more complexity and convert foo and bar to run in parallel... I don't have much understanding on how I can achieve this:
Question: How can we update the code below, so that bar and foo are run in parallel and therefore, output has more than 2 possible outcomes?
Note: this is purely for learning purposes... I want to see the race conditions occure.
let inputA = 10;
let inputB = 11;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
inputA++;
inputB = inputB * inputA;
inputA = inputA + 3;
});
foo( () => {
inputB--;
inputA = 8 + inputB;
inputB = inputA * 2;
});
setTimeout(() => {
// This Parallel code should have more than 2 outputs;
console.log(inputA, inputB);
}, 2000);
The race condition you're looking to invoke isn't possible in ordinary Javascript, luckily. Any time you have a synchronous function, once control flow is passed to that function, that function is absolutely guaranteed to run to the end (or throw) before any other Javascript in the environment runs.
For example, given 1000 tasks which are scheduled by setTimeout to occur in the next 1-2 seconds (randomly), and you also invoke the following function after 1.5 seconds:
const fn = () => {
console.log('1');
for (let i = 0; i < 1e8; i++) {
}
console.log('2');
};
Once fn starts, it will run all of its code (synchronously) before the next random function runs. So even if the random functions call console.log, it is still guaranteed that, in the above situation, 2 will be logged right after 1.
So, in your original example, there are only 2 possibilities:
bar's callback runs first, and completely finishes, then foo's callback runs, and completely finishes. Or:
foo's callback runs first, and completely finishes, then bar's callback runs, and completely finishes.
Nothing else is possible, even if the random timeouts land on exactly the same number. Control flow can only be in exactly one place in the code at any given time.
For your original question, you want to see them run in parallel. You can use Promise.all to run multiple async tasks, it will waits for all async tasks resolved and return an output array.
Promise.all executes async tasks by iterating them(in series), not technically executes them in parallel, but they are running in parallel. It will give you a result when all async tasks resolved or rejected if any one of them failed.
Or you can just run them 1 by 1 without Promise.all. they won't block each other, so still in parallel, but you Promise.all just help you handle callback results in one place.
The output will be either 12 or 20, depends on random timeout you set for bar and foo functions.
For race condition, only the setTimeout functions are asynchronous, but all operations in callbacks are synchronous and non-blocking, so the thread won't jump from operations in one callback to another callback unless all operations in that callback are finished.
But in JS, you can still have data race when using SharedArrayBuffer which needs Atomics object to prevent data race.
let output = 0;
let inputA = 10;
let inputB = 11;
const bar = (cb) => setTimeout(cb, Math.random() * 1000);
const foo = (cb) => setTimeout(cb, Math.random() * 1000);
bar( () => {
inputA++;
inputB = inputA;
output = inputA + 1;
});
foo( () => {
inputB--;
inputA = inputB;
output = inputB * 2;
});
Promise.all([bar(),foo()])
.then(output => console.log('foo and bar tasks finished with output ',output));
setTimeout(() => {
console.log('output variable value: ', output)
}, 2000);
Related
I want to be able to call setInterval (or something similar) at two different lengths, alternating.
For example, running a function after 5 seconds, then 1 second, then 5 seconds again, and so on.
Is this possible? I tried a function that alternates the value, but it didn't seem to work.
let num = 5000
function alternateNum() {
if (num === 5000) { num = 1000 }
else { num = 5000 }
}
setInterval(() => {
// ...
alternateNum()
}, num);
JS timers have a very complicated history.
Using a recursive setTimeout invocation is a simple and elegant solution as long (as your runtime implements tail call optimization).
Separate from the issue of recursion is the issue of timer drift. This is covered in the YouTube video JavaScript counters the hard way - HTTP 203 if you'd like an accessible introduction.
In many JS engines (e.g. V8) setInterval will handle drift correction for you, so there's actually an advantage to using it over recursively invoking setTimeout. (Check the millisecond timestamps in the console messages in the snippet below to verify this.)
In order to determine the constant interval argument you'll need for setInterval, you'll need to find the greatest common factor of your delay durations. Once you have this value, you can use it as the base interval delay, and keep track of your interval state to determine whether you should switch to the next interval delay, run your other code, etc. Here's a minimal example:
const durations = [1000, 5000];
// If you can't determine this in advance and use a constant value,
// then you can calculate it at runtime using a function:
const gcf = 1000; // or const gcf = findGreatestCommonFactor(durations);
let durationIndex = 0;
let elapsed = 0;
function update () {
elapsed += gcf;
const ready = elapsed === durations[durationIndex];
if (ready) {
elapsed = 0;
durationIndex = (durationIndex + 1) % durations.length;
}
return ready;
}
setInterval(() => {
const ready = update();
if (!ready) return;
// Do your interval task, for example:
console.log('tick');
}, gcf);
The problem with setInterval() is that the time is taken into account just once. You can use setTimeout() with recursion instead:
function doAction(flipFlop) {
setTimeout(() => {
console.log(flipFlop ? 'flip' : 'flop');
doAction(!flipFlop);
// do some other action...
}, flipFlop ? 1000 : 3000);
}
doAction(true);
Watch out though if you have a long running process, this recursion gets deeper and deeper.
I think this method is the easiest:
setInterval(() => {
console.log("first");
setTimeout(() => console.log("second"), 750);
}, 2000);
This creates an interval that alternates between 1250 and 750 milliseconds.
The problem with your code
let num = 5000
function alternateNum() {
if (num === 5000) { num = 1000 }
else { num === 5000 }
}
setInterval(() => {
// ...
alternateNum()
}, num);
The last few lines (the setInterval) call are only getting called once with the initial value of num and thus any future changes to num won't be reflected in the setTimeout call.
How to fix it
You should use setTimeout within the function that has your code and call your function recursively:
const doStuff = (time = 1000) => {
// Your code here
// generate the next time to wait
const nextTime = time === 5000 ? 1000 : 5000;
// call the function again after waiting `time` milliseconds
setInterval(() => doStuff(nextTime), time);
}
Then you would call doStuff to start it. If you wanted to start it immediately with the next one happening after 1 second you could do:
doStuff();
Or if you wanted to call it after 5 seconds with the next one happening one second after that:
setTimeout(doStuff, 5000);
The difference here compared to your code is that the variable that represents the time is being used over and over again as it changes instead of just once on initial code execution.
I need to execute 3 functions in a 1 sec delay.
for simplicity those functions are :
console.log('1');
console.log('2');
console.log('3');
I could do this: ( very ugly)
console.log('1')
setTimeout(function () {
setTimeout(function () {
console.log('2')
setTimeout(function () {
console.log('3')
}, 1000)
}, 1000)
}, 1000)
Or I could create an array of functions and use setInterval with global counter.
Is there any elegant way of doing this ?
(p.s. function no.2 is not dependent on function number 1... hence - every sec execute the next function.).
You can use something like this with setTimeout:
var funcs = [func1, func2, func3],
i = 0;
function callFuncs() {
funcs[i++]();
if (i < funcs.length) setTimeout(callFuncs, 1000);
}
setTimeout(callFuncs, 1000); //delay start 1 sec.
or start by just calling callFuncs() directly.
Update
An setInterval approach (be aware of the risk of call stacking):
var funcs = [func1, func2, func3],
i = 0,
timer = setInterval(callFuncs, 1000);
function callFuncs() {
funcs[i++]();
if (i === funcs.length) clearInterval(timer);
}
Assuming you run it on a modern browser or have added support for array.map this is quite concise:
[func1, func2, func3].map(function (fun, index) {
setTimeout(fun, 1000 + index * 1000);
}
setTimeout(function(){console.log('1')}, 1000);
setTimeout(function(){console.log('2')}, 2000);
setTimeout(function(){console.log('3')}, 3000);
There is a new type of function declaration called generators in es6 (a.k.a ecmascript 6, es2015). It is incredibly useful for this situation, and makes your async code look synchronous. es6 is the latest standard of JavaScript as of 2015. It works on modern browsers but you can use Babel and its javascript polyfill to use generators now even on older browsers.
Here is a tutorial on generators.
The function myDelayedMessages below is an example of a generator. Run is a helper function that takes a generator function which it calls and provides a function to advance the generator as the first argument of the generator function that it called.
function delay(time, callback) {
setTimeout(function () {
callback();
}, time);
}
function run(generatorFunction) {
var generatorItr = generatorFunction(resume);
function resume(callbackValue) {
generatorItr.next(callbackValue);
}
generatorItr.next()
}
run(function* myDelayedMessages(resume) {
for(var i = 1; i <= 3; ++i) {
yield delay(1000, resume);
console.log(i);
}
});
This is an overview of the code which is similar to the above tutorial's final overview.
run takes our generator and creates a resume function. run creates a
generator-iterator object (the thing you call next on), providing
resume.
Then it advances the generator-iterator one step to kick
everything off.
Our generator encounters the first yield statement
and calls delay.
Then the generator pauses.
delay completes 1000ms later and calls resume.
resume tells our generator to advance a single step.
Our generator continues from the spot it yielded at then console.logs i, which is 1, then continues the loop
Our generator encounters the second call to yield,
calls delay and pauses again.
delay waits 1000ms and ultimately
calls the resume callback. resume advances the generator again.
Our generator continues from the spot it yielded at then console.logs i, which is 2, then continues the loop.
delay waits 1000ms and ultimately
calls the resume callback. resume advances the generator again.
Our generator continues from the spot it yielded at then console.logs i, which is 3, then continues and the loop finishes.
There are no more calls to yield, the generator finishes executing.
with async/await
const pause = _ => new Promise(resolve => setTimeout(resolve, _));
async function main() {
await pause(1000);
console.log('one');
await pause(1000);
console.log('two');
await pause(1000);
console.log('three');
}
main();
note this works in a loop too
const pause = _ => new Promise(resolve => setTimeout(resolve, _));
async function main() {
for (let i = 0; i < 3; ++i) {
await pause(1000);
console.log(i + 1);
}
}
main();
I think most simple way to do this is to create some closures within a function.
First i'll recall that you have big interest in using setInterval, since the overhead of the setTimeout might have it trigger 10ms off target. So especially if using short (<50ms) interval, prefer setInterval.
So we need to store the function array, the index of latest executed function, and an interval reference to stop the calls.
function chainLaunch(funcArray, time_ms) {
if (!funcArray || !funcArray.length) return;
var fi = 0; // function index
var callFunction = function () {
funcArray[fi++]();
if (fi==funcArray.length)
clearInterval(chainInterval);
} ;
var chainInterval = setInterval( callFunction, time_ms);
}
Rq : You might want to copy the function array ( funcArray = funcArray.slice(0); )
Rq2 : You might want to loop within the array
Rq3 : you might want to accept additionnal arguments to chainlaunch. retrieve them with var funcArgs = arguments.slice(3); and use apply on the functions : funcArray[fi++].apply(this,funcArgs);
Anyway the following test works :
var f1 = function() { console.log('1'); };
var f2 = function() { console.log('2'); };
var f3 = function() { console.log('3'); };
var fArr = [f1, f2, f3];
chainLaunch(fArr, 1000);
as you can see in this fiddle : http://jsfiddle.net/F9UJv/1/
(open the console)
There are here two methods. One with setTimeout and anotherone with setInterval. The first one is better in my opinion.
for(var i = 1; i++; i<=3) {
setTimeout(function() {
console.log(i);
}, 1000*i);
}
// second choice:
var i = 0;
var nt = setInterval(function() {
if(i == 0) return i++;
console.log(i++);
if(i>=3) clearInterval(nt);
}, 1000);
I am new to javascript and reading about closures currently. I was trying to implement an example to better understand the concept.
This is the snippet:
function getValue() {
let a = 0
const runner = async () => {
a += 1
}
setInterval(runner, 100)
return () => {
return a
}
}
let value = getValue()
console.log(value()) -----> logging statement 1
// some long operation
console.log(value()) -----> logging statement 2
However, the output that I see is:
1
1
I expected the two logging statements to output two different values. My understanding is that once getValue() is called, the runner function would execute at regular intervals. The variable a would get updated and hence the 2nd console.log should have printed an incremented value of a.
You are almost there except missed the main part.
Operations are happening too fast. It does not take 100 milliseconds to run the second iteration so the value remains the same.
Try to wait at least 100 milliseconds since the interval you have needs that amount of time to increment.
As shown in the example below.
function getValue() {
let a = 0
const runner = async () => {
a += 1
}
setInterval(runner, 100)
return () => {
return a
}
}
let value = getValue()
console.log(value())
// operations are happening too fast
setTimeout(() => console.log(value()), 100);
setTimeout(() => console.log(value()), 200);
setTimeout(() => console.log(value()), 300);
function getValue() {
let a = 0
const runner = () => {
a += 1
console.log("Runner's A: " +a)
}
setInterval(runner, 100)
return () => {
return a
}
}
let value = getValue()
console.log(value());// -----> logging statement 1
// some long operation
console.log(value());// -----> logging statement 2
https://jsbin.com/geponejuba/edit?js,console,output
as pointed out above js is single threaded, and so the way it works is, your log statements are executed 1st and if there is nothing else to do, then setTimeout/setInterval are executed (this is a extremely simplified statement, You can read about "Event loop" for a detailed version)
on the example jsbin i shared you can see the setInterval is getting called and is indeed updating the value.
This is what my code looks like:
var fnInterval = setInterval(function() {
let b = true
if (b) {
console.log("hi")
} else {
console.log("bye")
}
b = !b
}, 1000);
clearTimeout(fnInterval, 10000)
I am a newbie to JavaScript and my aim here is to console log a message every 1 second for a total duration of 10 seconds, but during each interval I want my message to toggle its value between "hi" and "bye" . How can I do it? (as of now it displays the value for the default boolean and doesn't change later)
Move the flag variable out of the function:
let b = true;
const fnInterval = setInterval(function() {
if (b) {
console.log("hi");
} else {
console.log("bye");
}
b = !b
}, 1000);
To stop the timer after 10000 milliseconds, wrap the call to clearInterval in a setTimeout:
setTimeout(() => clearInterval(fnInterval), 10000);
Meanwhile, note that the return value of setInterval is not a function but a number, so it may be misleading to call it fnInterval.
First of all, declare let b = true outside of the callback function. It's re-initialized on each call otherwise.
Secondly, the 10000 in clearTimeout(fnInterval, 10000) isn't a valid parameter. clearTimeout(timeoutId) accepts only the first parameter and clears the timeout passed in immediately. You'd need a setTimeout to trigger this after 10 seconds, if that's your goal. But that causes a race condition between the two timeouts -- imprecision can mean you'll miss some of the logs or wind up with extra logs.
Using a counter is one solution, as other answers show, but usually when I'm using complex timing with setInterval that requires clearing it after some number of iterations, I refactor to a generic promisified sleep function based on setTimeout. This keeps the calling code much cleaner (no callbacks) and avoids messing with clearTimeout.
Instead of a boolean to flip a flag back and forth between two messages, a better solution is to use an array and modulus the current index by the messages array length. This makes it much easier to add more items to cycle through and the code is easier to understand since the state is implicit in the counter.
const sleep = ms => new Promise(res => setInterval(res, ms));
(async () => {
const messages = ["hi", "bye"];
for (let i = 0; i < 10; i++) {
console.log(messages[i%messages.length]);
await sleep(1000);
}
})();
setInterval() is stopped by clearInterval() not clearTimeout(). Details are commented in code below.
// Define a counter
let i = 0;
// Define interval function
const fnCount = setInterval(fnSwitch, 1000);
function fnSwitch() {
// Increment counter
i++;
// if counter / 2 is 0 log 'HI'
if (i % 2 === 0) {
console.log(i + ' HI');
// Otherwise log 'BYE'
} else {
console.log(i + ' BYE');
}
// If counter is 10 or greater run fnStop()
if (i >= 10) {
fnStop();
}
};
function fnStop() {
// Stop the interval function fnCount()
clearInterval(fnCount);
};
JavaScript async/await and Synchronous JavaScript both look similar and making the program to wait, then what is the use of async/await ?
For example
Synchronous JavaScript Code
function getStudentList() {
return ['Adam', 'Ben'];
}
function findStudent(who) {
const list = getStudentList();
const found = list.some(student => student === who);
console.log(found); // logs true
}
findStudent('Adam');
Asynchronous JavaScript using async/await
function getStudentList() {
return new Promise(resolve => {
setTimeout(() => resolve(['Adam', 'Ben'], 1000);
});
}
async function findStudent(who) {
const list = await getStudentList();
const found = list.some(student => student === who);
console.log(found);
}
findStudent('Adam');
in above code, const list = getStudentList(); and const list = await getStudentList(); both functions like synchronous code.
Thank you
A synchronous function/code is a code that gets executed line by line, task by task. No matter how much time a specific task takes to be executed the next one doesn't start until the current one is finished, in other words the execution flow is paused/stopped in each task and starts back again when the current task is finished. i.e:
console.log("foo");
console.log("bar");
// executing this code will log the following:
// foo
// bar
An asynchronous function/call is a code that starts executed at a certain point in time and then the next tasks get executed even if the first one is not finished. So it gets executed in, say, a parallel timeline or thread if you want to call it.
An example of this would be having a function that logs "ASYNC" after 3 seconds (suppose we have a pre-defined delay function called sleep(seconds)):
async function foo() {
sleep(3); // wait for 3 seconds
console.log("ASYNC");
}
console.log("foo");
foo();
console.log("bar");
// executing this code will log the following:
// foo
// bar
// ASYNC
Note that bar was logged before ASYNC even though the function that logs the latter was called first. Basically asynchronous function don't stop the execution flow and just start executing the code inside it and goes on to the next line of code after the call, regardless of whether the async code was finished or not.
There are times when you actually want the execution flow to stop in asynchronous code. Say you have a variable called x and you want to have a sum operation function called calculate() and its result is stored to x but the function is an online request that is executed with a certain amount of delay (for example, 1 second).
let x = 0;
async main() {
console.log(x);
x = calculate(5,8);
console.log(x);
}
main();
// executing this code will log the following:
// 0
// {}
Note that the value of x didn't become 13 as expected but we got some weird result instead. This is because the request started executing and then the value of x was logged before the request has returned the sum, as a result (and depending on how a the request works) the value of x will be some random object or null/undefined until the request is finished and then x will be equal to 13
To fix this, we have to use the keyword await that stops the asynchronous execution flow and waits for another asynchronous function to end until the flow is back on.
let x = 0;
async main() {
console.log(x);
x = await calculate(5,8);
console.log(x);
}
main();
// executing this code will log the following with a 1 second delay between each log:
// 0
// 13
Side note: the await keyword cannot be used in synchronous context like
await someAsyncFunction();
and is only used before asynchronous functions (the ones that are defined with the async keyword).So await is only used with asynchronous functions in asynchronous contexts like this:
async someAsyncFunction() {
// some tasks
await anotherAsyncFunction();
// some more tasks
}
Your first example is synchronous. It's also faster.
function getStudentList(){
return ['Adam', 'Ben'];
}
function findStudent(who){
const list = getStudentList(), found = list.includes(who);
console.log(found); // logs true
}
console.time('sync'); findStudent('Adam'); console.timeEnd('sync');
Your second example is asynchronous, and slower by about a second. But, you wouldn't be able to test that, unless your async function is resolved first. That's why I used .then.
function getStudentList(){
return new Promise(resolve=>{
setTimeout(() => resolve(['Adam', 'Ben']), 1000);
});
}
async function findStudent(who){
const list = await getStudentList(), found = list.includes(who);
console.log(found);
}
console.time('async');
findStudent('Adam').then(()=>{
console.timeEnd('async');
});
You will notice that arrayInstance.includes is a better solution to find if something is in an Array. By the way, it makes no sense to use async in this particular case.
Here is an async example that makes more sense:
function delay(milliseconds){
return new Promise(resolve=>{
setTimeout(()=>{
resolve(milliseconds);
}, milliseconds);
});
}
async function logicalAsync(){
let wait;
console.time('test');
wait = await delay(1000);
console.timeEnd('test');
console.log('milliseconds = '+wait);
}
logicalAsync();
Of course, synchronous code takes however long it takes to execute, with all the code below it waiting on the above code to be finished processing. Asynchronous code allows the code below to execute (possibly) before the code above it is finished.