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.
Related
I'm wondering if it's possible to move a set of variables from the global scope to a nested scope. Is it possible to use a closure to achieve this in the following context? It must be, right?
The let variables probably shouldn't be within the renderInfo() scope because renderInfo() is called multiple times at load, and this cannot be avoided. Each time renderInfo() is called, render() renders multiple elements, all of which have a click event listener added to them. Hence the variables also can't be here with the way the code is currently structured.
I've tried turning clickToSort() into a closure, but I run into issues every time. I can't figure out how to allow all of the elements with the click event listener to share access to the let variables.
let
sortNameAscending =
sortFreeAscending =
sortSizeAscending = true
// Called multiple times at load.
function renderInfo(a,b,c,d) {
// Renders multiple elements, and adds an event listener to them, each call.
function render(){
// The event listener is added to multiple elements
// that are also rendered herein.
ele.addEventListener('click', (e)=>clickToSort(e, cls, 'aString'))
}
// This function is added to the click event of tons of elements.
function clickToSort(e, cls, dataProperty) {
// How do I move the let variables from the global
// scope to here, so that they behave as if they
// are in the global scope? Is it possible with a
// closure?
// let
// sortNameAscending =
// sortFreeAscending =
// sortSizeAscending = true
// I imagine the following code should be wrapped in
// its own scope, but the scope must have access to
// the arguments of clickToSort(), and the let variables
// which should behave as if they are global.
if (cls.includes('whatever')) {
sortNameAscending = !sortNameAscending
} else if (cls.includes('whatever2')) {
sortFreeAscending = !sortFreeAscending
} else {
sortSizeAscending = !sortSizeAscending
}
}
}
I've tried the following, but it doesn't wanna work.
let
sortNameAscending =
sortFreeAscending =
sortSizeAscending = true
function renderInfo(a,b,c,d) {
function render(){
// The event listener is added to multiple elements
// that are also rendered herein.
ele.addEventListener('click', (e)=>clickToSort(e, cls, 'aString'))
}
function clickToSort(e, cls, dataProperty) {
let
sortNameAscending =
sortFreeAscending =
sortSizeAscending = true
;(function whatevs(){
if (cls.includes('whatever')) {
sortNameAscending = !sortNameAscending
} else if (cls.includes('whatever2')) {
sortFreeAscending = !sortFreeAscending
} else {
sortSizeAscending = !sortSizeAscending
}
)()
}
}
I'm not sure why, although it probably has something to do with the fact that I've bound the clickToSort() function to the elements, instead of perhaps returning a function?
You could wrap the whole function into an immediately invoked function expression. That would ensure closure on your three variables while letting your calls share the same data.
In your anonymous function, you can declare your enclosed variables and return the function which was initially called renderInfo. When your code is executed, those three variables will be declared and kept inside the anonymous function's scope, then renderInfo will be given the value of a function.
// Called multiple times at load.
const renderInfo = (function() {
let
sortNameAscending =
sortFreeAscending =
sortSizeAscending = true
return function(a, b, c, d) {
// Renders multiple elements, and adds an event listener to them, each call.
function render() {
// The event listener is added to multiple elements
// that are also rendered herein.
ele.addEventListener('click', (e) => clickToSort(e, cls, 'aString'))
}
// This function is added to the click event of tons of elements.
function clickToSort(e, cls, dataProperty) {
if (cls.includes('whatever')) {
sortNameAscending = !sortNameAscending
} else if (cls.includes('whatever2')) {
sortFreeAscending = !sortFreeAscending
} else {
sortSizeAscending = !sortSizeAscending
}
}
}
})()
A simple example of using an IIFE (Immediately Invoked Function Expression) to create a closure is the following:
const add = (function() {
let sum = 0;
return function() {
return ++sum;
}
})()
console.log(add())
console.log(add())
console.log(add())
console.log(add())
I've made a promise based function which crawls up a hierarchy until it reaches the top, and resolves with an object containing the structure. My only gripe with the code is that I modify variables outside the function body, meaning that it is not a pure function. I've looked into JavaScript closures, and I fully grasp trivial uses of them. But I'm struggling to figure out how/if they can help make my function pure. My attempts at making a closure so far have only overwritten the variables, not modified them. Here is the code in question using global variables:
/* I want to move these variables inside function body to purify 'getPriorRows'*/
let priorRows = {}, level = 0;
const getPriorRows = id => new Promise(resolve => {
fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level++] = result;
if (result.parentID) resolve(getPriorRows(result.parentID));
else resolve(priorRows);
});
});
getPriorRows('123432').then(result => console.log(result));
Any input on the matter is greatly appreciated.
Pass the values as arguments:
function getPriorRows(id, priorRows = {}, level = 0) {
return fetch(`/api/org/${id}`).then(result => {
/* global varaiables are modified here */
priorRows[level] = result;
if (result.parentID) return getPriorRows(result.parentID, priorRows, level+1);
else return priorRows;
});
}
getPriorRows('123432').then(result => console.log(result));
You can use either default parameters or a wrapper function, you don't even need a closure:
function getAll(id) { return getPriorRows(id, {}, 0); }
Also the I removed the Promise constructor antipattern.
You should be able to enclose the entire function and its "external" variables in a new function:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => new Promise(
...
);
return getNext(id);
}
That said, your creation of an explicit new Promise in each iteration is a Promise anti-pattern:
function getPriorRows(id) {
let priorRows = {}, level = 0;
const getNext = id => fetch(`/api/org/${id}`).then(result => {
priorRows[level++] = result
if (result.parentID) {
return getNext(result.parentID));
} else {
return priorRows;
}
});
return getNext(id);
}
Either way, the advantage of wrapping the state like this is that you could now have multiple calls to getPriorRows proceeding in parallel without interfering with each other.
EDIT second code edited to fix a copy&paste error with the recursion - you must call the inner function recursively, not the outer one.
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
}
I am trying to create some nested 'while' loops within my promises. I start to run into trouble in my innerFunc when I am executing log.asyncCall().then(...) my IDE is telling me that right away, the arrow function will jump straight to the .then() containing return innerFunc() without even going into the if statement. I think the race conditions are not executing in the order I want them to.
Summary of what I am trying to achieve:
According to outerFunc I want to perform innerFunc this 3 times.
What innerFunc does call .asyncCall() over and over again as long as log is NOT null. If innerFunc cannot produce log, exit the 'while' loop.
I'm also having trouble finding a good way to debug promises. I am currently using the debug feature with WebStorm.
var i = 0
const outerFunc = () => {
return new Promise(resolve => {
if (i < 3) {
i++
const innerFunc = () => {
return new Promise(resolve => {
return log.asyncCall().then(lg => {
if (lg) {
// some logic
} else {
resolve()
}
}).then(() => {
// continue iterating innerFunc()
return innerFunc();
});
})
};
// first iteration innerFunc()
return innerFunc().then(() => {
// once innerFunc() is complete, reiterate outerFunc()
return outerFunc();
});
} else {
resolve();
}
});
}
You are using a Promise anti-pattern, since you're wrapping a promise in another promise.
I would strongly advise taking innerFunc out of the inner scope, and rewriting it thus:
const doLogging = () => {
return log.asyncCall().then(result => {
if (result) {
// do stuff
...
return doLogging();
}
});
}
There's no need for an else branch - the inner arrow function will implicitly return undefined which will then be wrapped by .then into a Promise.
You then want to run this count times:
const repeatPromise = (f, count) => {
if (count > 0) {
return f().then(() => repeatPromise(f, count - 1));
} else {
return Promise.resolve();
}
}
const outerFunc = () => repeatPromise(doLogging, 3);
In this function the else branch is necessary to ensure that the final result is always a Promise.
(NB: as written the repeatPromise function won't work properly if the passed function doesn't return a Promise)
I'm relatively new to coding in JavaScript, and I've came across a problem. I like to nest functions to keep things orderly, but how would I exit from a parent function from inside a child function?
example:
function foo1() {
function foo2() {
//return foo1() and foo2()?
}
foo2();
}
See update under the fold
You can't. You can only return from the child function, and then return from the parent function.
I should note that in your example, nothing ever calls foo2 (As of your edit, something does). Let's look at a more real example (and one that comes up a lot): Let's say we want know if an array contains an entry matching some criterion. A first stab might be:
function doesArrayContainEntry(someArray) {
someArray.forEach(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does <-- This is wrong
}
});
return false; // No it doesn't
}
You can't directly do that. Instead, you have to return from your anonymous iterator function in a way to stop the forEach loop. Since forEach doesn't offer a way to do that, you use some, which does:
function doesArrayContainEntry(someArray) {
return someArray.some(function(entry) {
if (entryMatchesCondition(entry)) {
return true; // Yes it does
}
});
}
some returns true (and stops looping) if any call to the iterator function returns true; it returns false if no call to the iterator returned true.
Again, that's just one common example.
You've referred to setInterval below, which tells me that you're almost certainly doing this in a browser environment.
If so, your play function almost certainly has already returned by the time you want to do what you're talking about, assuming the game has any interaction with the user other than alert and confirm. This is because of the asynchronous nature of the environment.
For example:
function play() {
var health = 100;
function handleEvent() {
// Handle the event, impacting health
if (health < 0 {
// Here's where you probably wanted to call die()
}
}
hookUpSomeEvent(handleEvent);
}
The thing is, that play will run and return almost immediately. Then the browser waits for the event you hooked up to occur, and if it does, it triggers the code in handleEvent. But play has long-since returned.
Make a note whether the parent function should also return.
function foo1() {
bool shouldReturn = false;
function foo2() {
shouldReturn = true; // put some logic here to tell if foo1() should also return
return;
}
if (shouldReturn) {
return;
} else {
// continue
}
}
It only says that you can't return the parent function in the child function, but we can do a callback and make it happen.
function foo1(cb = () => null) {
function foo2() {
cb();
}
foo2();
}
foo1(() => {
// do something
});
We can use Promises for this:
const fun1 = async () => {
const shouldReturn = await new Promise((resolve, reject) => {
// in-game logic...
resolve(true)
})
if(shouldReturn) return;
}
if you wanna return from the parent function, then just resolve with true
Based on your comment, something like this might work as a main game loop.
function play() {
var stillPlaying = true;
while(stillPlaying) {
... play game ...
stillPlaying = false; // set this when some condition has determined you are done
}
}