Given the following javascript code (or something equivalent):
var buf = [];
setInterval(function () {
buf.push("token");
// If buf has something pushed here we are screwed
if (buf.length == 1) {
sendCriticalLog();
}
});
setInterval(function () {
buf.push("other token");
});
Is there a way to ensure that the function of the first interval is atomic with regard to buf?
The only method I could come up with is:
function atomic(lock, cb){
var finish = function () {
lock.callbacks = lock.callbacks.slice(1);
if (lock.callbacks.length) {
lock.callbacks[0].call();
}
};
cb = cb.bind(null, finish);
if ((lock.callbacks = (lock.callbacks || []).concat([cb])).length == 1) {
// Nothing is running
lock.callbacks[0]();
};
}
var buf = [];
setInterval(function () {
atomic(buf, function () {
buf.push("token");
// If buf has something pushed here we are screwed
if (buf.length == 1) {
sendCriticalLog();
}
});
});
setInterval(function () {
atomic(buf, function () {
buf.push("other token");
});
});
But that is under the assumption that ((lock.callbacks = (lock.callbacks || []).concat([cb])).length == 1) will be guaranteed to be handled atomically. If, for example, concat is written in plain javascript this will probably not work...
JavaScript is not multithreaded, so your callbacks are in fact already "atomic". buf can only be altered between calls to the callback.
Related
I understand how to use setTimeout function, but I can't find a way to create a function like it.
I have an example:
setTimeout(() => {
console.log('3s');
}, 3000);
while(1);
The result is setTimeout callback never call so I think it use the same thread like every js other functions. But when it's check the time reach or not? and how it can do that?
Updated
To avoid misunderstanding I update my question.
I can't find a way to create a async function with callback after specify time (without using setTimeout and don't block entire thread). This function setTimeout seen like a miracle to me. I want to understand how it work.
Just for the game since I really don't see why you couldn't use setTimeout...
To create a non-blocking timer, without using the setTimeout/setInterval methods, you have only two ways:
event based timer
run your infinite loop in a second thread
Event based timer
One naive implementation would be to use the MessageEvent interface and polling until the time has been reached. But that's not really advice-able for long timeouts as this would force the event-loop to constantly poll new tasks, which is bad for trees.
function myTimer(cb, ms) {
const begin = performance.now();
const channel = myTimer.channel ??= new MessageChannel();
const controller = new AbortController();
channel.port1.addEventListener("message", (evt) => {
if(performance.now() - begin >= ms) {
controller.abort();
cb();
}
else if(evt.data === begin) channel.port2.postMessage(begin);
}, { signal: controller.signal });
channel.port1.start();
channel.port2.postMessage(begin);
}
myTimer(() => console.log("world"), 2000);
myTimer(() => console.log("hello"), 100);
So instead, if available, one might want to use the Web Audio API and the AudioScheduledSourceNode, which makes great use of the high precision Audio Context's own clock:
function myTimer(cb, ms) {
if(!myTimer.ctx) myTimer.ctx = new (window.AudioContext || window.webkitAudioContext)();
var ctx = myTimer.ctx;
var silence = ctx.createGain();
silence.gain.value = 0;
var note = ctx.createOscillator();
note.connect(silence);
silence.connect(ctx.destination);
note.onended = function() { cb() };
note.start(0);
note.stop(ctx.currentTime + (ms / 1000));
}
myTimer(()=>console.log('world'), 2000);
myTimer(()=>console.log('hello'), 200);
Infinite loop on a different thread
Yes, using Web Workers we can run infinite loops without killing our web page:
function myTimer(cb, ms) {
var workerBlob = new Blob([mytimerworkerscript.textContent], {type: 'application/javascript'});
var url = URL.createObjectURL(workerBlob);
var worker = new Worker(url);
worker.onmessage = function() {
URL.revokeObjectURL(url);
worker.terminate();
cb();
};
worker.postMessage(ms);
}
myTimer(()=>console.log('world'), 2000);
myTimer(()=>console.log('hello'), 200);
<script id="mytimerworkerscript" type="application/worker-script">
self.onmessage = function(evt) {
var ms = evt.data;
var now = performance.now();
while(performance.now() - now < ms) {}
self.postMessage('done');
}
</script>
And for the ones who like to show off they know about the latest features not yet really available (totally not my style), a little mention of the incoming Prioritized Post Task API and its delayed tasks, which are basically a more powerful setTimeout, returning a promise, on which we can set prioritization.
(async () => {
if(globalThis.scheduler) {
const p1 = scheduler.postTask(()=>{ console.log("world"); }, { delay: 2000} );
const p2 = scheduler.postTask(()=>{ console.log("hello"); }, { delay: 1000} );
await p2;
console.log("future");
}
else {
console.log("Your browser doesn't support this API yet");
}
})();
The reason callback of setTimeout() is not being called is, you have while(1) in your code which acts as infinite loop. It will keep your javascript stack busy whole time and that is the reason event loop will never push callback function of setTimeout() in stack.
If you remove while(1) from your code, callback for setTimeout() should get invoked.
setTimeout(() => {
console.log('3s');
}, 3000);
To create your own setTimeout function, you can use the following function, setMyTimeout() to do that without using setTimeout.
var foo= ()=>{
console.log(3,"Called after 3 seconds",new Date().getTime());
}
var setMyTimeOut = (foo,timeOut)=>{
let timer;
let currentTime = new Date().getTime();
let blah=()=>{
if (new Date().getTime() >= currentTime + timeOut) {
clearInterval(timer);
foo()
}
}
timer= setInterval(blah, 100);
}
console.log(1,new Date().getTime());
setMyTimeOut(foo,3000)
console.log(2,new Date().getTime());
Following is the implementation of custom setTimeout and setInterval, clearTimeout and clearInterval. I created them to use in sandbox environments where builtin setTimeout and setInterval doesn't work.
const setTimeouts = [];
export function customSetTimeout(cb, interval) {
const now = window.performance.now();
const index = setTimeouts.length;
setTimeouts[index] = () => {
cb();
};
setTimeouts[index].active = true;
const handleMessage = (evt) => {
if (evt.data === index) {
if (window.performance.now() - now >= interval) {
window.removeEventListener('message', handleMessage);
if (setTimeouts[index].active) {
setTimeouts[index]();
}
} else {
window.postMessage(index, '*');
}
}
};
window.addEventListener('message', handleMessage);
window.postMessage(index, '*');
return index;
}
export function customClearTimeout(setTimeoutId) {
if (setTimeouts[setTimeoutId]) {
setTimeouts[setTimeoutId].active = false;
}
}
const setIntervals = [];
export function customSetInterval(cb, interval) {
const intervalId = setIntervals.length;
setIntervals[intervalId] = function () {
if (setIntervals[intervalId].active) {
cb();
customSetTimeout(setIntervals[intervalId], interval);
}
};
setIntervals[intervalId].active = true;
customSetTimeout(setIntervals[intervalId], interval);
return intervalId;
}
export function customClearInterval(intervalId) {
if (setIntervals[intervalId]) {
setIntervals[intervalId].active = false;
}
}
Hi you can try this. ]
HOpe it will help. Thanks
function customSetTimeOut (callback, ms) {
var dt = new Date();
var i = dt.getTime();
var future = i + ms;
while(Date.now() <= future) {
//do nothing - blocking
}
return callback();
}
customSetTimeOut(function(){
console.log("Timeout success");
},1000);
Hey I'm trying to understand functional programming, stay current etc.
I have this gigantic nested node function tha tI'm attempting to refactor into modules. Each function passes an argument to the next, some are async api calls. I've gotten as far as breaking it down into multiple functions but they're still not really modular as each one calls the next function.
I'm wondered if there's a design pattern to totally untangle them so that the names of the other functions don't appear in the functions but can be passed as callbacks or something. Or maybe that's not even appropriate in this case?
I wrote some simpler fake code that approximates my actual problem so you don't have to sift through endless irrelevant specifics:
function1Async();
function function1Async() {
var theArray = [];
var theData = "";
var theInfo = "";
Async.get( /* api */, function(error, data, response) {
if (error) {
function4(); // function call
}
theData = data[0].infos;
for (i in data) {
theInfo = theData[i].info;
theArray.push(theInfo);
}
function2Async(theArray); // function call
});
}
function function2Async(array) {
var theArray = [];
var theObject = {}
var arrayLength = array.length;
for (let i = 0; i < arrayLength; i++) {
search = '"' + array[i] + '"';
T.get(/* api */, {q: trend, search: 50}, function (error, data, response) {
if (error) {
theArray.push("error");
}
else {
theObject = {
"data": data
}
theArray.push(theObject);
}
if (theArray.length == arrayLength) {
function3(theArray); // function call
}
});
}
}
function function3 (array) {
var theArray = array;
if (theArray.includes("error") == true) {
function5(); // function call
}
else {
function4(theArray); // function call
}
}
function function4(array) {
var file = 'files/data.json';
jsonfile.writeFile(file, array, function(err) {
function5(); // function call
});
}
function function5() {
var file = 'files/data.json';
jsonfile.readFile(file, function(err, obj) {
return res.json(obj);
});
}
Feel free to chime in with any suggestions even if they're not totally related to the question. Like I said I'm just trying to understand this stuff altogether. Thanks.
I have a function that looks like this:
const XML = '.xml code';
var array = [];
var i = 0; //counter
XMLExtract(XML, 'loc', false, (error, element) => {
if (error) {
throw new Error(error);
}
array[i] = element;
console.log(array[i]);
function_name(array[i]); //imported function from external .js
i++;
});
Basically I want to run function() to return the response that it gives and then run function() again with the new parameter. However, the above code doesn't work, it just overlaps.
I've also checked previous solutions: https://stackoverflow.com/a/5010339 but I think I don't really know how to implement it. Any suggestion?
UPDATE: external.js
module.exports = function() {
this.function_name = function() {
(async function() {
...
await instance.exit();
}());
};
};
I have this (simplified here) code when the "reading" function calls itself many times (it is actually reading a file by chunks). The problem is that by doing so (this is what I think), the chain is lost and the "then" of the upload() function is never reached.
In other words, the 'hola' text is never displayed in the console. What is the proper way to write this?
Thanks,
https://jsfiddle.net/yewdmh2o/1/
reading = function() {
start++;
upload();
};
var start = 0;
var end = 5;
var myArray = [3,5,7];
myArray.reduce(function(promise, item) {
return upload()
.then(function(){
console.log('hola');
})
})
function upload() {
if (start < end) {
console.log('doing')
reading();
} else {
console.log('finished')
return Promise.resolve();
}
}
EDIT:
Solved with the help of #Bergi, by always returning a promise it can keep the chaining process and give the desired outcome, like this:
reading = function() {
start++;
return upload();
};
var start = 0;
var end = 5;
var myArray = [3,5,7];
myArray.reduce(function(promise, item) {
return upload()
.then(function(){
console.log('hola');
})
})
function upload() {
if (start < end) {
console.log('doing')
return reading();
} else {
console.log('finished')
return Promise.resolve();
}
}
I want to call a function after an asynchronous for loop iterating through values of an Javascript object finishes executing. I have the following code
for (course in courses) {
var url = '...' + courses[course];
request(url, (function (course) {
return function (err, resp, body) {
$ = cheerio.load(body);
//Some code for which I use object values
};
})(course));
}
This can be done in vanilla JS, but I recommend the async module, which is the most popular library for handling async code in Node.js. For example, with async.each:
var async = require('async');
var courseIds = Object.keys(courses);
// Function for handling each course.
function perCourse(courseId, callback) {
var course = courses[courseId];
// do something with each course.
callback();
}
async.each(courseIds, perCourse, function (err) {
// Executed after each course has been processed.
});
If you want to use a result from each iteration, then async.map is similar, but passes an array of results to the second argument of the callback.
If you prefer vanilla JS, then this will work in place of async.each:
function each(list, func, callback) {
// Avoid emptying the original list.
var listCopy = list.slice(0);
// Consumes the list an element at a time from the left.
// If you are concerned with overhead in using the shift
// you can accomplish the same with an iterator.
function doOne(err) {
if (err) {
return callback(err);
}
if (listCopy.length === 0) {
return callback();
}
var thisElem = listCopy.shift();
func(thisElem, doOne);
}
doOne();
}
(taken from a gist I wrote a while back)
I strongly suggest that you use the async library however. Async is fiddly to write, and functions like async.auto are brilliant.
A possible simple JS solution would be to do something like this.
var courses = {
lorum: 'fee',
ipsum: 'fy',
selum: 'foe'
};
var keys = Object.keys(courses);
var waiting = keys.length;
function completedAll() {
console.log('completed all');
}
function callOnCourseComplete(course, func) {
console.log('completed', course);
waiting -= 1;
if (!waiting) {
func();
}
}
var delay = 10000;
keys.forEach(function(course) {
var url = '...' + courses[course];
console.log('request', url);
setTimeout((function(closureCourse) {
return function( /* err, resp, body */ ) {
// Some code for which I use object values
callOnCourseComplete(closureCourse, completedAll);
};
}(course)), (delay /= 2));
});
Update: Probably a better Javascript solution would be to use Promises
const courses = {
lorum: 'fee',
ipsum: 'fy',
selum: 'foe',
};
function completedAll() {
console.log('completed all');
}
function callOnCourseComplete(courseName) {
console.log('completed', courseName);
}
let delay = 10000;
const arrayOfPromises = Object.keys(courses).map(courseName => (
new Promise((resolve, reject) => {
const url = `...${courses[courseName]}`;
console.log('request', url);
setTimeout((err, resp, body) => {
if (err) {
reject(err);
}
// Some code for which I use object values
resolve(courseName);
}, (delay /= 2));
}))
.then(callOnCourseComplete));
Promise.all(arrayOfPromises)
.then(completedAll)
.catch(console.error);