cjava
cjava

Reputation: 658

Javascript Compose order of execution of functions

const compose = (f, g) => (...args) => f(g(...args));
const bigTask  = (...fns) => fns.reduce(compose);
bigTask(
  task1,
  task2,
  task3,
  task4
)(user, {data:'data'})

my understanding is the function reduce first picks task1 and task2 and then passes them to compose. The result of the above and task3 is picked as the next arguments to the compose function. The individual tasks: task1, task2... are not dependent on each other so it's not a recursive functions. So the execution order for me should be task1, task2, task3 and then task4.

Instead the actual execution order is task4, task3, task2, task1. Why!!! bangs own head in frustation!!!!
I also understand that f(g(x)) will execute g(x) first and then f(). But can somebody please explain me why task4 is executed first. I see there is giant gap between my understanding of compose and "how it actually works"

Upvotes: 1

Views: 315

Answers (2)

Jonas Wilms
Jonas Wilms

Reputation: 138237

I think the main confusion stems from bad variable names, namely f andg, it might get easier to understand if we write:

 const compose = (prev, curr) => (...args) => prev(curr(...args));

On every iteration, a function gets created, whereas prev points to the function created in the previous iteration:

 // 1
 task1
 // 2 (two)
 (...args) => task1(task2(...args))
 // 3 (three)
 (...args) => two(task3(...args))
 // 4 (four)
 (...args) => three(task4(...args))
 // unfolded
 (...args) => task1(task2(task3(task4(...args)))

As suggested already there are three possible solutions:

(1) swap curr and prev so that curr gets called first.

(2) Use reduceRight instead of reduce

(3) Get rid of all the unneccessary closures and do:

 const bigTask = (...fns) => arg => fns.reduce((acc, fn) => fn(acc), arg);

Upvotes: 3

Isa
Isa

Reputation: 418

Call fns.reduce(compose); mean something like this

[task1, task2, task3, task4].reduce(function(accumulator, currentValue, currentIndex, array) {
  return accumulator(currentValue())
})

// first step
var s1 = function() {task1(task2())}
// second step
var s2 = function() {s1(task3())}
// third step
var s3 = function() {s2(task4())}

s3()->s2(task4)->s1(task3(task4))->task1(task2(task3(task4())))

Upvotes: 2

Related Questions