Receiving returns from asynchronous functions - javascript

I've searched through multiple questions, but I have failed to find one that matches my case.
The following is the simplified version of my code:
function one() {
let a;
// does some fetching; takes about 1s
a = 1; // value is obtained from above line
return a;
}
function two() {
let b;
// does some fetching; takes about 2s
b = 2; // value is obtained from above line
return b;
}
function three() {
let c;
// does some fetching; takes about 0.5s
c = 3; // value is obtained from above line
return c;
}
var variables = [];
function final() {
variables.push(one());
variables.push(two());
variables.push(three());
}
final();
console.log(variables);
What above code gives is [], while the desired output is [1, 2, 3](the order doesn't matter).
I've recognized that the solution is deeply related to async/await and promises(return new Promise(...)), but the implementation mostly fails, either because the await keyword does nothing, or promise comes out <pending>.
Also, through several experiments I've found that the return keyword also runs asynchronously, which means that one(), two() and three() almost certainly returns undefined.
Is there a way to get the return values in a synchronous manner?
Edit 1: The original code works as follows: (1)three different functions fetches json from different APIs which takes some seconds, (2) each functions returns the parsed data from the json, then (3) let the returned value be pushed onto var variables = []; array. I should have added this text in the first place.

Related

Calling a function next to another function javascript explanation

Can someone please explain to me this bit of code
return pipe(...fns)(this);
I understand if we didn't have (this), so we returned the reduced functions but (this) confused me.
Full source code here. line 72
https://stackblitz.com/edit/typescript-hv4ntb
It is the same as this:
const pipeFunction = pipe(...fns); // pipe returns a function
const result = pipeFunction(this); // call the returned function with this as argument
return result; // return the result
So if you ever see something like variable(...)(...) you should assume that the variable evaluates to a function that returns a function, like this perhaps:
const variable = (a) => (b) => a + b;
variable(4)(2);
// ==> 6
const partial = variable(8)
[1, 2, 3].map(partial);
// ==> [9, 10, 11]
Javascript is said to have first-class functions (you can read more here on MDN), meaning that you can do with functions what you can do with other types. Storing them into a variable, passing them as argument to other functions, as well as using them as return value from other functions.
The thinking is, just think of functions as values.
In your use-case, a function is just being returned from another function:
function multiply(times) {
return function(number) {
return number * times;
};
}
// as you can see, we are storing the "returned function"
// resulted from calling `multiply` into the `double` variable.
const double = multiply(2);
console.log(
double(5)
);
Now, of course, you could short-circuit the double variable,
and just call the returned function straight away.
function multiply(times) {
return function(number) {
return number * times;
};
}
console.log(
multiply(2)(5)
);

Javascript array only running function that returns a promise on one element of array

Ive posted other questions but feel I should simplify things
I have one function that sets a context and calls a second function to draw the lines on the context.
I have this:
var arr = [];
which is populated like this:
arr = [context,pre];
while pre looks like pre = [[{x:n,y:m}],[{x:j,y:k}]];
So, basically, I have an array, pre, containing arrays of coordinates. That array is pushed with a context into arr
arr is returned and pushed into a final array, lets say final_arr, which now should look like this: final_arr = [[context1,pre1],[context2,pre2],...]
My goal is to loop through final_arr and draw lines on different contexts, determined by the context in the array. For example, the first iteration will access final_arr[0] and contain context1,pre1. These two values are sent to a function, wrap(context, pre) that returns a promise. Inside this wrap function, another function is called, animate(pre[i]). this function takes each element in pre, which corresponds to an array of coordinates, and actually draws the line using animation frames. animate() also returns a promise.
Currently, only one of the paths is being drawn, which seems to be because only one value of final_arr is being used, even though I am iterating through it
My attempts to iterate:
final_arr.reduce((a,c) => a.then(() => wrap(c[0],c[1])), Promise.resolve());
and
var temp = Promise.resolve();
var i = 0;
for (i = 0; i < arr.length; i++){
//window.alert(arr[i].length)
var ct = arr[i][0];
var line = arr[i][1];
temp.then(() => wrap(ct,line));
}
and here are the functions being called:
/*
* Animation function draws a line between every point
*/
var animate = function(p){
return new Promise(function(resolve) {
t = 1;
var runAnimation = function(){
if(t<p.length){
context.beginPath();
context.moveTo(p[t-1].x,p[t-1].y);
context.lineTo(p[t].x,p[t].y);
context.stroke();
t++;
requestAnimationFrame(function(){runAnimation()});
} else {
resolve()
}
};
runAnimation();
});
}
function wrap(ctx, lines){
return new Promise(function(resolve) {
var counter = 0;
t = 1;
var getAnimation = function(){
if(counter < lines.length){
context = ctx;
lines.reduce((a, c) => a.then(() => animate(c)), Promise.resolve());
counter++;
} else {
resolve()
}
};
getAnimation();
});
}
The context variable set in wrap is a global variable for the js file
I hope the question asked this way provides clarity as to what I am having a problem with
Thank you for any help
Edit:
Attempted fiddle
Edit2:
Oddly enough this works
if(final_arr.length == 1){
wrap(final_arr[0][0], final_arr[0][1]);
} else if (final_arr.length == 2){
wrap(final_arr[0][0], final_arr[0][1]).then(wrap(final_arr[1][0], final_arr[1][1]));
} else if (final_arr.length == 3){
wrap(final_arr[0][0], final_arr[0][1]).then(wrap(final_arr[1][0], final_arr[1][1])).then(wrap(final_arr[2][0], final_arr[2][1]));
}
But when using this, the lines are drawn at the same time (which is okay, but not preferred)
edit: just spotted the missing resolve inside the if statement of wrap => the returned Promise will never be resolved...
I recommend to start with a much simpler version of wrap before making any micro-optimizations:
function wrap(ctx, lines){
return new Promise(function(resolve) {
lines.forEach(p => animate(p, ctx));
resolve();
});
}
callbacks to requestAnimationFrame are called after finishing all microtasks (i.e. after all Promises) - see When will requestAnimationFrame be executed?
so the value of the global variable context will be the same for all of the callbacks, i.e. the same line drawn multiple times or a race condition or something depending on internals of the context
I would get rid of globals, using only function params and locals:
var animate = function(p, ctx) {
var t ...
... ctx.beginPath()

How does a reference in closure and currying work in js?

The following program confuses me very much, the console.log() were added by me.
function add(a) {
console.log("1");
var total = a;
console.log("2");
var _fn = function (b) {
console.log("3");
total += b;
console.log("4");
return _fn;
};
console.log("5");
_fn.toString = _fn.valueOf = function () {
console.log("6");
return total;
};
console.log("7");
console.log("_fn: " + _fn);
return _fn;
}
When I ran add(1)(2), console showed:
1
2
5
7
6
_fn: 1
3
4
6
ƒ 3
My questions are:
1) in var _fn = function (b) {...}, what does _fn refer to in "return _fn" statement? If it refers to itself, then isn't it in infinite recursion like this
var _fn = function (b) {
total += b;
return function (b) {
total += b;
return function (b) {
total += b;
return _fn;
.
.
.
}
}
}
2) In console, it showed "_fn: 1" which means 1 was returned, but apparently, _fn (the function) was returned so that the calculation could keep going. So there is a conflict between the actual returned _fn and the value shown in console.
in var _fn = function (b) {...}, what does _fn refer to in "return _fn" statement?
The return statement says the when this function is called, the return value from the function will be (a reference to) the function object itself. Note it returns the function object, without calling it. Just the object, no call. At least not yet anyway....
If it refers to itself, then isn't it in infinite recursion like this ...
No, because the return value, the function, is not immediately called. It's kind of like doing this:
function g() {return g}
g()
Running that does not go into an infinite loop. You call the function g, and you get back g. You can do g()()()()()()() yourself, but that still "stops." Recursion is when a function calls itself, not returns itself!
In console, it showed "_fn: 1" which means 1 was returned, but apparently, _fn (the function) was returned so that the calculation could keep going. So there is a conflict between the actual returned _fn and the value shown in console.
Well, it's probably not correct to say 1 was returned; instead the code forces all console.logs (and similar) to produce the current value of total. At the time you did your console.log, total had the value of your first argument, namely 1. You were pretty smart to print out all those numbers, so it should help your understanding. Check out that after the 7 was printed, you had not yet done the subsequent call in which the addition was done. That's why you saw 1.

why the variable is undefined

I could access the variable 'savedCards ' from the first promise, it has some value. Inside the second promise it's undefined but the variable 'eCard' has value. Please explain me why ?
saveCard(eCard: IEcards) {
var savedCards: IEcards[] = [];
this.storage.get("key").then((value) => {
if (value.saves == undefined) {
var saves = savedCards;
value.saves = saves;
}
savedCards = value.saveCard; // have value and can be accessed
console.log(savedCards);
}).then((data) => {
console.log(savedCards); // savedCards is undefined but eCard.id has value
this.globalProvider.IsCardExist(eCard.id, savedCards).then((data) => {
if (!data.response) {
this.globalProvider.AddEcardToStorage("saves", eCard);
}
});
});
}
When you need to access the intermediate values in your chain, you should split your chain apart in those single pieces that you need. Instead of attaching one callback and somehow trying to use its parameter multiple times, attach multiple callbacks to the same promise - wherever you need the result value.
function getExample() {
var a = promiseA(…);
var b = a.then(function(resultA) {
// some processing
return promiseB(…);
});
return Promise.all([a, b]).then(function([resultA, resultB]) {
// more processing
return // something using both resultA and resultB
});
}
[Edit]
You want to know why, and here is the answer: ES6 came with generator functions, which allow to break the execution apart in pieces at arbitrarily placed yield keywords. Those slices can be run after each other, independently, even asynchronously - and that's just what we do when we want to wait for a promise resolution before running the next step.
Your code is resolving the second promise before the first one. You can not assure that your code will work as you want by using "then()". If you want a synchronous resolution, you should go for another way.
[Edit 2]
Try to use await and see if you are able to solve your problem. More info here: http://2ality.com/2017/08/promise-callback-data-flow.html

Getting Value from Function in Javascript

I am a beginner at Javascript and am running into a little problem. It is a homework problem, but it is graded based on completion only, so I am only trying to figure out the right answer for myself.
I am supposed to define a function, repeatUntil, that takes in two other functions, say f(returns a number) and g (returns a boolean value). The functionality of repeatUntil is to repeat function f at least once until g returns true.
Here is what I have so far:
function repeatUntil(f, cond) {
var f1;
do{
f1 = f;
return f1;
}
while(cond(f1()));
}
And here is the tester/how we call it:
var print = console.log;
var r = repeatUntil(function(x) { return x + x }, function(x) { return x >= 20 })
print(r(2))
print("Expected: 32")
The function runs, but my problem right now is storing the updated value of x from the repeatUntil function. Right now the function only runs once, and the condition is not updated because I cannot pass in the updated value of x into the function g. I tried putting the result of f() into a variable, but it will only return a function and not a number.
Any help/advice would be greatly appreciated. Thank you!
Combining the existing comments into an answer.
Since you need to call r(), your function needs to return another function.
The do loop will run exactly once because you return in the body of the loop
Code
function repeatUntil(f, cond) {
return function() {
while(cond(f()));
}
}

Categories