So the problem is I have to get the sum of first n natural numbers but the conditions are
1.Using a helper function which returns a promise
2.Cannot use + operator inside the main function (only allowed in the in the helper function).
3.Cannot use async - await
The so-far solution I came to is
nat_sum = (n) =>{
let i=1,res=0;
while(i<=n){
sumP(res,i++).then( data => {
res = data;
console.log(res);
});
console.log("res is ",i, res);
}
};
//Helper function is
function sumP(x,y){
// Always resolves
return new Promise((resolve,reject)=>{
resolve(x+y);
});
}
But the problem is, the loop just initializes all the call to the sumP with initial value of res i.e 0 , which means it just doesn't wait for the former promise to resolve and update the res variable.
Same problem solved using a callback is as follows (You can ignore this, Just a insight to the problem!) :
function sumc(x,y,callme){
return callme(x,y);
}
nat_sumC = (n)=>{
let i=1,res=0;
while(i<=n){
res = sumc(res,i++,sum);
}
return res;
}
You can use recursion
function sumP(x, y) { //Helper function
// Always resolves
return new Promise((resolve, reject) => {
resolve(x + y);
});
}
const naturalSum = (n, i = 1, res = 0) => {
sumP(res, i).then(data => {
if (i == n) {
console.log(data);
return;
}
naturalSum(n, ++i, data)
});
};
naturalSum(5)
naturalSum(6)
You should use recursion:
const nat_sum = n => {
let i = 0;
let recurse = res =>
sumP(res)
.then(recurse)
.catch(e => console.info(`Sum of numbers 1 to ${n} is ${e}`));
function sumP(x) {
return new Promise((resolve, reject) => {
if (i < n) {
resolve(x + ++i);
} else reject(x);
});
}
recurse(0); //Recurse from 0
};
[4, 5, 6, 15].forEach(nat_sum);
Find the answer to the smaller problem, n - 1, then add n to answer using sumP -
function natSum(n){
if (n === 0)
return Promise.resolve(0)
else
return natSum(n - 1).then(answer => sumP(n, answer))
}
// provided helper function
function sumP(x,y){
return new Promise((resolve,reject)=>{
resolve(x+y);
});
}
natSum(4).then(console.log) // 10
natSum(5).then(console.log) // 15
natSum(6).then(console.log) // 21
Rewriting with arrows, we can remove a lot of syntactic noise -
const sumP = (x, y) =>
Promise .resolve (x + y)
const natSum = n =>
n === 0
? Promise .resolve (0)
: natSum (n - 1) .then (r => sumP (n, r))
natSum (4) .then (console.log) // 10
natSum (5) .then (console.log) // 15
natSum (6) .then (console.log) // 21
Using async and await only hides the Promise primitives like Promise.resolve and .then -
const sumP = async (x, y) =>
x + y //<-- returns promise because of "async" keyword
const natSum = async n =>
n === 0
? 0
: sumP (n, (await natSum (n - 1)))
natSum (4) .then (console.log) // 10
natSum (5) .then (console.log) // 15
natSum (6) .then (console.log) // 21
Related
I am trying to re-implement redux compose function, instead of using reduce i use a for loop, here is my code:
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
let result;
for (let i = funcs.length - 1; i > -1; i--) {
result = result
? (...args) => funcs[i](result(...args))
: (...args) => funcs[i](...args);
}
return result;
}
// test
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x * 10;
}
function fn3(x) {
return x - 1;
}
console.log(compose(fn3, fn2, fn1)(10)); // 109
It is expected to log 109 since (10 + 1) * 10 - 1 is 109, however it gives me such error:
RangeError: Maximum call stack size
Looks like i am doing some recursion but all i did is just a for loop, no sure where is the problem of my code?
I think the issue is like the below example:
a = () => 2;
a = () => 3 * a();
console.log(a);
// this prints () => 3 * a() in console
// so when you call a(), it will call 3 * a(), which will again call 3 * a() and so on
// leading to infinite recursion
My solution is slightly different using bind function based on this reference link: https://stackoverflow.com/a/6772648/4688321.
I think bind creates a new copy of the function result and binds it to a new object. Not using bind leads to recursion because then the code becomes like the above example, result calls result.
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
let result;
for (let i = funcs.length - 1; i > -1; i--) {
if (i == funcs.length - 1)
result = (...args) => funcs[i](...args);
else {
let temp = result.bind({});
result = (...args) => funcs[i](temp(...args));
}
}
return result;
}
// test
function fn1(x) {
console.log("fn1");
return x + 1;
}
function fn2(x) {
console.log("fn2");
return x * 10;
}
function fn3(x) {
console.log("fn3");
return x - 1;
}
//console.log(compose(fn3, fn2, fn1));
let ret = compose(fn3, fn2, fn1);
console.log(ret(10)); // 109
Rather than trying to combine functions at the time of compose, it seems much easier to combine them at the time the resulting function is called:
function compose(...funcs) {
if (funcs.length === 0) {
return (arg) => arg
}
return function (...args) {
let result = funcs .at (-1) (...args)
for (let i = funcs.length - 2; i > -1; i--) {
result = funcs [i] (result)
}
return result
}
}
// test
function fn1(x) {
return x + 1;
}
function fn2(x) {
return x * 10;
}
function fn3(x) {
return x - 1;
}
console.log(compose(fn3, fn2, fn1)(10)); // 109
However, again, reduce make for a much cleaner implementation:
const compose = (...fns) => (arg) =>
fns .reduceRight ((a, fn) => fn (a), arg)
or if you want to allow the rightmost function to receive multiple variables, then
const compose = (...fns) => (...args) =>
fns .reduceRight ((a, fn) => [fn (...a)], args) [0]
I have a higher-order function in js that takes an array of asynchronous functions, n parameter which is passed to the first function and a callback function that prints the result from the promise returned by the last function. First function will pass the result to the 2nd one but it needs to wait until it's finished and so on. The problem is only the first promise gets resolved and the rest of the promises are pending. How can I "chain" these promises for the callback not to print NaN but actual value ("chain" in this function, not manually, I know I can do this with .then() but if I have a bigger array it's not gonna be effective)?
const functionsInSequence = (funTab, cb) => (n) => {
const lastPromise = funTab.reduce((acc, fn) => {
return new Promise(resolve =>
fn(acc).then(value => {
resolve(value);
})
)
}, n);
cb(lastPromise);
};
const functions = [
async x => x * 2,
async x => x * 3,
async x => x * 4,
];
const myCb = (promise) => {
promise.then(value => console.log("val:", value));
}
functionsInSequence(functions, myCb)(2);
You can use async/await syntax to make code more readable. You can loop through the functions array and await for each response before continue. You can do it like this:
const functions = [
async x => x * 2,
async x => x * 3,
async x => x * 4,
];
const resolveAll = async (input) => {
let result = input;
for (let index = 0; index < functions.length; index++) {
result = await functions[index](result);
console.log(`${index+1}. function result: `, result)
}
return result;
}
resolveAll(5).then((result)=>{
console.log('Final Result: ', result);
})
The program description
Create 3 functions:
FuncA – will receive a string and returns it’s length
FuncB – will receive an array of strings and returns their total lengths (using
funcA) after 2 seconds.
FuncC - will receive an array of arrays of strings and returns their total lengths
(using FuncB)
My solution is
function funcA(s)
{
return s.length
}
function funcB(arr)
{
return new Promise(resolve =>
{
setTimeout(() =>
{
let total = 0;
arr.forEach(element => {
total += funcA(element)
});
resolve(total)
},2000)
})
}
function funcC(arr)
{
return new Promise(resolve =>
{
let isFirst = true
//a <=> total
let total = arr.reduce(async (a,b) =>
{
if(isFirst) {
isFirst = false
return (await funcB(a) + await funcB(b))
}
else {//a <=> total
return (a + await funcB(b))
}
})
resolve(total)
})
}
The running is:
funcC([["aa","bbb","tyui"],["ccc"],["dfghj","aedtfr"]]).then(x => console.log(x))
The result is:
[object Promise]11
What is the problem?
This is really convoluted.
Don't put business logic in the setTimeout callback. Only resolve the promise, then do the work in a promise then callback or after an await.
Always pass an initial value to reduce! This will make it work with empty arrays, and it will obviate the need for that really weird isFirst logic.
total already is a promise. Don't unnecessarily wrap it in a new Promise!
These suggestions will lead to
function funcA(s) { return s.length }
function funcB(arr) {
return new Promise(resolve => {
setTimeout(resolve, 2000);
}).then(() => {
let total = 0;
arr.forEach(element => {
total += funcA(element)
});
return total;
});
}
function funcC(arr) {
return arr.reduce(async (a,b) => {
return await a + await funcB(b)
}, Promise.resolve(0))
}
However, reduce is not really suited for asynchronous work. You should rather use the looping approach in funcC, and use reduce in funcB where it fits much better:
async function funcB(arr) {
await new Promise(resolve => {
setTimeout(resolve, 2000);
});
return arr.reduce((total, element) => total + funcA(element), 0);
}
async function funcC(arr) {
let total = 0;
for (const b of arr) {
total += funcB(b);
}
return total;
}
[fixed the answer]
you shall do await a instead of a everywhere
function funcC(arr)
{
return new Promise(resolve =>
{
let isFirst = true
//a <=> total
let total = arr.reduce(async (a,b) =>
{
if(isFirst) {
isFirst = false
return (await funcB(await a) + await funcB(b))
}
else {//a <=> total
return (await a + await funcB(b))
}
})
resolve(total)
})
}
I have this code, which is counting array elements sum with recursion, but when the array is too big it throwing Maximum call stack size exceeded error.
var a = new Array(10 ** 7);
a.fill(0);
function sum(arr, i = 0) {
if(i === arr.length) {
return 0;
}
return arr[i] + sum(arr, i + 1);
}
sum(a);
So I need to change it somehow to work normally for all cases and I thought I can make it work asynchronously with Promises, but it always returning Promise pending.
var a = new Array(10 ** 7);
a.fill(0);
var sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
return resolve(0);
}
return sum(arr, i + 1).then(el => el + arr[i]);
}, 0);
});
sum(a);
How can I fix it?
Any help would be appreciated!
You are only resolving the case where i is arr.length, so all the chained promises remain pending forever. Return won´t automatically resolve it for us, so need to be explicit:
var a = new Array(10);
a.fill(0);
a[0] = 3;
var sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
resolve(0);
} else {
resolve(sum(arr, i + 1).then(el => el + arr[i]));
}
}, 0);
});
sum(a).then(console.log)
There are some solutions for your issue with using native tasks. this one is using Promise to schedule a microtask:
(function() {
function sum(arr, i = 0) {
if(arr.length === i) {
return Promise.resolve(0);
}
return Promise.resolve(null)
.then(() => sum(arr, i + 1))
.then((x) => x + arr[i]);
}
sum(a).then(s => console.log(s));
}());
but this will force the engine to wait until the execution is completed. So for huge arrays, I wouldn't recommend you doing this on the main thread.
You can also do the following:
(function() {
function sum(arr, i = 0) {
if(arr.length === i) {
return Promise.resolve(0);
}
return new Promise(resolve => {
setTimeout(() => {
sum(arr, i + 1).then(x => resolve(x + arr[i]));
});
});
}
sum(a).then(s => console.log(s));
}());
then with make a few changes to this code and making it more elegant, we can do the following:
(function() {
const defer = () => new Promise((resolve) => setTimeout(resolve));
async function sum(arr, i = 0) {
if(arr.length === i) {
return 0
}
await defer();
return await sum(arr, i + 1) + arr[i];
}
sum(a).then(s => console.log(s));
}());
and if your environment supports tail recursion you can change this to use it: http://2ality.com/2015/06/tail-call-optimization.html
UPDATE
actually, there is one more way to do this. rxjs library provides a queue scheduler which can help you make a recursive logic to be iterative without making many changes in your code. I've created an example of your sum method here.
I have no idea why you would want to use promises and make the function async. But if you do you need to resolve both cases:
const sum = (arr, i = 0) => new Promise((resolve) => {
setTimeout(() => {
if(i === arr.length) {
return resolve(0);
}
return sum(arr, i + 1).then(el => resolve(el + arr[i]));
}, 0);
});
Now this also returns a promise. Once you've gone async you can never go back. You need to use the returned promise to get the return value:
sum([1, 2, 3, 4]).then(return => console.log(return));
Its better to not make it async. ES6 supports tail recursion so you can just make it like this:
function sum(arr) {
function helper(acc, i) {
if (i<0) {
return acc;
}
return helper(arr[i]+acc, i-1);
}
return helper(0, arr.length - 1);
}
Since the order doesn't matter I took the liberty to sum the values in reverse. Now believe it or not, but that helper already exists in the language as an abstraction that gives you the value of each element and the acc in each iteration. Thus you can do this instead:
function sum(arr) {
return arr.reduce((acc, val) => acc + val, 0)
}
setTimeout is not a solution for the stack overflow problem. Managing your stack is a matter of sequencing your function calls. You can do this in a variety of ways, the most intuitive being the loop/recur trampoline.
const loop = f =>
{ let acc = f ()
while (acc && acc.tag === recur)
acc = f (...acc.values)
return acc
}
const recur = (...values) =>
({ tag: recur, values })
const sum = (xs = []) =>
loop ((result = 0, i = 0) => // initial state
i >= xs.length // exit condition
? result // return value
: recur (result + xs[i], i + 1)) // next state
const tenMillionNumbers =
Array.from (Array (1e7), (_,n) => n)
console.time ('recursion is not slow')
console.log (sum (tenMillionNumbers))
console.timeEnd ('recursion is not slow')
// => 49999995000000
// recursion is not slow: 2171ms
I cover many other techniques for this problem here.
Stack-safe recursion is something I write about a lot, with almost 30 answers on the topic
var a = new Array(1000);
a.fill(1);
function sum(arr, i = 0, res = 0, resolve) {
if(!i) return new Promise((resolve) => sum(arr, i + 1, res + arr[i], resolve), 0);
if(i === arr.length) return resolve(res);
setTimeout(function(){
sum(arr, i + 1, res + arr[i], resolve);
}, 0);
}
sum(a).then(console.log);
You need to use an iterative process instead of recursive one. So instead of accumulating calls, you'd compute your sum on each iteration call. This can be achieved by using a helper function which will get (sum, array, currentValue)
If for whatever reason you need to use recursion and don't mind this taking a long, you could use a timeout; however I would absolutely not recommend using this. I'm mostly posting it because it's possible.
var arr = new Array(10 ** 7);
arr.fill(0);
var len = arr.length,
idx = 0,
sum = 0,
sumFn = () => {
setTimeout(() => {
sum += arr[idx];
idx++;
if (idx < len) {
sumFn();
} else {
//complete
}
}, 1);
}
sumFn();
How do I make chain use a spread-parameter (...chain) instead of an array?
let newData,
newArgs,
chain = function (data, myargs) {
if (myargs.length === 0) {
return;
}
newData = myargs[0](data);
myargs.splice(0, 1);
newArgs = myargs;
// newargs === array
chain(newData, newArgs);
},
add1 = (num) => {
return num + 1;
};
When I call the function, I have to do it like this: chain(5, [add1, add1, add1]).
But I want: chain(5, add1, add1, add1)
The problem is, that newArgs (the function parameter inside of the recursion) is converted into an array after the first function call.
https://jsfiddle.net/btx5sfwh/
You probably want to use rest arguments, then spread the array when calling the function recursively
let newData,
newArgs,
chain = (data, ...myargs) => {
if (myargs.length === 0) {
return data;
} else {
newData = myargs[0](data);
myargs.splice(0, 1);
newArgs = myargs;
chain(newData, ...newArgs);
}
},
add1 = (num) => {
return num + 1;
};
chain(5, add1, add1, add1);
console.log(newData); // 8
You can write chain in a much nicer way
const chain = (x, f, ...fs) =>
f === undefined
? x
: chain (f (x), ...fs)
const add1 = x =>
x + 1
console.log (chain (5, add1, add1, add1)) // 8
console.log (chain (5)) // 5
Rest parameter and spread arguments are somewhat expensive tho - we generally want to limit our dependency on these, but we don't want to sacrifice the API we want either.
Don't worry, there's an even higher-level way to think about this problem. We can preserve the variadic API but avoid the costly spread argument.
const chain = (x, ...fs) =>
fs.reduce((x, f) => f (x), x)
const add1 = x =>
x + 1
console.log (chain (5, add1, add1, add1)) // 8
console.log (chain (5)) // 5