Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Higher-order folder function using callbacks

I want to write a function that given a list of functions, will fold across that list, progressively passing in the result from each closure into the next one.

The function signature of the functions in the list would be something like (pseudo-code):

typealias DoneClosure = (Dictionary) -> Void
typealias Middleware = (Dictionary, DoneClosure) -> Void

I would have a list of type Middleware and I want to reduce from left to right, folding across the list and passing the result of each closure into the next closure.

let middleware1 = { acc, done in
    // do something with acc, modify it
    done(acc)
}

Each function would look similar to this, they'd modify the accumulator in some way and then pass the result to the done or next function.

What I'm looking for is a recursive function that can fold across the list using callbacks so it can be processed async. Can anyone help me out? (Language is irrelevant but JS or Swift is preferred).

like image 342
barndog Avatar asked Aug 17 '26 11:08

barndog


1 Answers

// does not handle empty middlewareList
const makeFunctionChain = function (middlewareList, initalDic) {
    const length = middlewareList.length;
    let i = 0;
    let middleware;

    const next = function (localAccumulator) {
        middleware = middlewareList[i];
        i += 1;
        if (i === length) {
            // there is no next
            // pass a do-nothing function 
            middleware(localAccumulator, function() {});
        } else {
            middleware(localAccumulator, next);
        }
    };
    next(initalDic);
};

// usage example

const middlewareAddOnions = function (food, next) {
    // simple middleware
    food["onions"] = 3;
    next(food);
};

const middlewareAddWater = function (food, next) {
    // here we use a new accumulator
    const newFood = Object.assign({}, food, {withWater: true});
    next(newFood);
};

const middlewareCook = function (food, next) {
    // next can also be called asynchronously.
    // here we use setTimeout
    const timeToCook = 1500;
    setTimeout(function () {
        food.cooked = true;
        next(food);
    }, timeToCook);
};

const middlewareServe = function (food, next) {
    // here we read the data
    // does not use next
    if (food.cooked) {
        console.log(`Time to eat: ${JSON.stringify(food)}`);
    } else {
        console.log(`Something went wrong: ${JSON.stringify(food)}`);
    }
};


// time to try it out
const food = {
    eggs: 4,
    potatoes: 12,
    // ...
};

makeFunctionChain([
    middlewareAddOnions,
    middlewareAddWater,
    middlewareCook,
    middlewareServe
    ], food);

As noted in the comments, there is also the possibility to use Promises to have a similar result.

like image 114
Walle Cyril Avatar answered Aug 19 '26 01:08

Walle Cyril



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!