admin管理员组

文章数量:1424893

I've currently got a expressJS app and I'm trying to retrieve information from an API using the getStats function. However, the profile route I've written seems to be multiple nested callbacks. How would I prevent this? Is there a way to make it get all of the stats, then assign them to variables after they all of the stats are retrieved?

function getStats(access_token, time_range, x, callback) {
    var stats = [];

    var options = {
        url: 'www.example',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        json: true
    }

    request(options, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            for (i = 0; i < body.items.length; i++) {
                stats.push(body.items[i].name);
            }
            return callback(stats);
        }
    })

}

app.get('/profile', function(req, res) {
    var access_token = 1234;

    getStats(access_token, 's', 'x', function(a){
        console.log(a);

        getStats(access_token, 's', 'y', function(b){
            console.log(b);

            getStats(access_token, 'm', 'x', function(c){
                console.log(c);

                getStats(access_token, 'm', 'y', function(d){
                    console.log(d);

                    getStats(access_token, 'l', 'x', function(e){
                        console.log(e);

                        getStats(access_token, 'l', 'y', function(f){
                            console.log(f);

                            res.send(a + "\n" + b + "\n" + c + "\n" + d + "\n" + e + "\n" + f);
                        });
                    });
                });
            });
        });
    });
});

I've currently got a expressJS app and I'm trying to retrieve information from an API using the getStats function. However, the profile route I've written seems to be multiple nested callbacks. How would I prevent this? Is there a way to make it get all of the stats, then assign them to variables after they all of the stats are retrieved?

function getStats(access_token, time_range, x, callback) {
    var stats = [];

    var options = {
        url: 'www.example.',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        json: true
    }

    request(options, function(error, response, body) {
        if (!error && response.statusCode == 200) {
            for (i = 0; i < body.items.length; i++) {
                stats.push(body.items[i].name);
            }
            return callback(stats);
        }
    })

}

app.get('/profile', function(req, res) {
    var access_token = 1234;

    getStats(access_token, 's', 'x', function(a){
        console.log(a);

        getStats(access_token, 's', 'y', function(b){
            console.log(b);

            getStats(access_token, 'm', 'x', function(c){
                console.log(c);

                getStats(access_token, 'm', 'y', function(d){
                    console.log(d);

                    getStats(access_token, 'l', 'x', function(e){
                        console.log(e);

                        getStats(access_token, 'l', 'y', function(f){
                            console.log(f);

                            res.send(a + "\n" + b + "\n" + c + "\n" + d + "\n" + e + "\n" + f);
                        });
                    });
                });
            });
        });
    });
});
Share Improve this question edited Sep 16, 2018 at 10:58 kockburn 17.7k10 gold badges90 silver badges143 bronze badges asked Sep 16, 2018 at 10:24 newangnewang 1992 gold badges2 silver badges8 bronze badges 1
  • 4 I would suggest that you look into Promises – SeinopSys Commented Sep 16, 2018 at 10:26
Add a ment  | 

3 Answers 3

Reset to default 7

Promises allow you to avoid the type of callback nesting like you've just shown. See my example which illustrates your example in Promise form:

function getStats(access_token, time_range, x, prevResult) {
    return new Promise((resolve, reject) => {
        if (prevResult) {
            resolve([...prevResult, "test", "test", "test"]);
        } 
        return resolve(["test", "test", "test"]);

    });
}

app.get('/profile', (req, res) => {
    var access_token = 1234;

    getStats(access_token, 's', 'x')
        .then((a) => {
            console.log(a);
            return getStats(access_token, 's', 'y', a);
        })
        .then((b) => {
            console.log(b);
            return getStats(access_token, 'm', 'x', b);
        })
        .then((c) => {
            console.log(c);
            return getStats(access_token, 'm', 'y', c);
        })
        .then((d) => {
            console.log(d);
            return getStats(access_token, 'l', 'x', d);
        })
        .then((e) => {
            console.log(e);
            return getStats(access_token, 'l', 'y', e);
        })
        .then((f) => {
            console.log(f);
            res.send(f.join("\n"));
    });
});

As you can see the nested callback structure and replace with a much more readable format. Read more about promises here.

The above could even be rewritten using Promise.all, which looks even nicer:

function getStats(access_token, time_range, x) {
    return new Promise((resolve, reject) => {
        return resolve(["test", "test", "test"]);
    });
}

app.get('/profile', (req, res) => {
    var access_token = 1234;

    Promise.all([
        getStats(access_token, 's', 'x'),
        getStats(access_token, 's', 'y'),
        getStats(access_token, 'm', 'x'),
        getStats(access_token, 'm', 'y'),
        getStats(access_token, 'l', 'x'),
        getStats(access_token, 'l', 'y')
    ]).then((values) => {
        console.log(values);
        res.send(values.join("\n"));
    });
});

Promises will help with that, or moving your callbacks into their own function expressions outside of the route, and then calling it in the route once will help.

The problem with that callback hell is that you are not using middlewares where the express app is supporting it.

Basically a way to write an express app is relying in the callbacks and so on. But the counterpart is that this will lead to as what you pointed out the callback hell.

This happens also with promises as well and it's called the promise hell when not properly handled.

The best way to avoid this two patterns are moving into the middlewares solutions.

function getAccesstoken(req, res, next) {
    if (!req.something) {
        return next('Error, something was not specified');
    }
    req.someRequestVariable = 'set_something';
    next();
}

function processRequest(req, res, next) {
    console.log(req.someRequestVariable);
    res.send(200);
}

app.use(function genericHandlerForEveryCall() {
    console.log('This is being called in every request');
})

app.get('/profile', getAccesstoken, processRequest);

本文标签: javascriptNodeJSHow to escape multiple nested callbacksStack Overflow