简体   繁体   中英

Why does wrapping a function in javascript cause recursion and stack overflow error?

I'm trying to wrap a list of functions over a callback function. Each of the function in the list takes in the value of the callback and returns a modified value. When I try to do this in a straight forward way, it does recursion, and the stack eventually runs out of space, and throws an error.

I tried solving the problem by using a wrapper function that took in a function, wrapped it with another one, and then returned it, and that solved the problem.

Look at the subscribe function:

class Observable {
    constructor() {
        this._subscribers = [];
        this._operators = [];
    }
    next(val) {
        this._subscribers.forEach(subscriber => {
            subscriber(val);
        });
    }
    subscribe(callback) {
        if (this._operators.length > 0) {
            let ogCallback;
            this._operators.forEach((operator, index) => {
                ogCallback = callback;
                /** ==== call stack full error =====
                 * callback = (val) => {              
                 *    ogCallback(operator(val));    
                 * };
                 */

                // This works
                callback = ((func) => {
                    const wrapper = (val) => {
                        func(operator(val));
                    };
                    return wrapper;
                })(ogCallback);
            });
            this._operators = [];
        }
        this._subscribers.push(callback);
    }
    pipe(operator) {
        this._operators.unshift(operator);
        return this;
    }
}

const observable = new Observable();
observable.pipe(val => val + 2).pipe(val => val * 2).subscribe(val => console.log(val));

observable.next(5);

Why does this happen? They both seem to be the same thing.

I suspect it's from the series of closures created by:

ogCallback = callback;
callback = (val) => {
   ogCallback(_function(val));
}

ogCallback and callback are global. After the initial iteration, callback has the value:

(val) => ogCallback(_function(val))

ogCallback has a closure to the global ogCallback , so it's value is whatever it was given from the last iteration, as does callback , potentially causing circular references.

The second example breaks the closure by creating a local variable func in the assigned function expression that is passed the value of ogCallback using an immediately invoked function expression (IIFE).

Original second example

_functions.forEach((_function, index) => {
    ogCallback = callback;
    callback = ((func) => {
        const wrapper = (val) => {
            func(_function(val));
        };
        return wrapper;
    })(ogCallback);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM