Higher order folder function using callbacks

I want to write a function that provided a list of functions that will add up through this list, gradually passing the result from each closure to the next.

The functional signature of the functions in the list would be like (pseudocode):

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

      

I would have a list of type Middleware

, and I want to shrink from left to right by folding the list and passing the result of each closure to the next closure.

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

      

Each function will look something like this: they modify the drive in some way, and then pass the result of the executed or next function.

What I'm looking for is a recursive function that can be folded through a list using callbacks so that it can be async processed. Can anyone help me? (Language doesn't matter, but JS or Swift is preferred).

+3


source to share


2 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, it is also possible to use Promises to get a similar result.

+3


source


I was able to figure it out using recursion (in Swift)



typealias Next = (JSON) -> ()
typealias JSON = [String: Any]
typealias Middleware = (JSON, Next) -> ()

func process(middlewareList: [Middleware], value: JSON, done: @escaping Next) {
    guard !middlewareList.isEmpty else {
        done(value)
        return
    }
    let middleware = middlewareList.first!
    let slice = Array(middlewareList[1..<middlewareList.count])
    middleware(value) { json in
        process(middlewareList: slice, value: json, done: done)
    }
}

let hook1: Middleware = { json, done in
    print("hook 1")
    done(json)
}

let hook2: Middleware = { json, done in
    print("hook 2")
    var copy = json
    copy["hi"] = "hi"
    done(copy)
}

process(middlewareList: [hook1, hook2], value: [:], done: { json in
     print(json)
})

      

0


source







All Articles