Node.js/Async - How to avoid callback hell with async?

前端 未结 3 1947
一生所求
一生所求 2021-01-12 04:55

I\'m new to Node.Js and JavaScript web development on the backend. I see that callbacks inside callbacks could be a pain and there are modules to avoid that. One of these mo

3条回答
  •  南方客
    南方客 (楼主)
    2021-01-12 05:33

    In my view, callback hell is really a mixture of two problems:

    • Anonymous inline functions.
    • Indentation.

    Either one in small amounts is fine, but together they make code rigid and unmaintainable. The solution to avoiding callback hell is to avoid these two things by:

    • Naming your functions and pulling them out of function calls.
    • Returning early to avoid intendation.

    Going by these principles, your code can be rewritten as:

    function check_auth_user(username, password, done) {
    
        // Make a new client and open the connection.
        function connect(callback) {
            var client = new pg.Client("pg://user:pass@127.0.0.1/database");
    
            client.connect(function (err) {
                if (err) {
                    console.error('could not connect to postgres', err);
                    return callback(err);
                }
    
                callback(null, client);
            });
        }
    
        // Query the database.
        function query(callback, results) {
            var client = results.client;
            var q = 'select * from "user" where username = $1 and password = $2';
            var params = [username, password];
    
            client.query(q, params, function (err, result) {
                if (err) {
                    console.error('error running query', err.stack || err.message);
                    return callback(err);
                }
    
                callback(null, result);
            });
        }
    
        // Do stuff with the result of the query.
        function handleQueryResult(callback, results) {
            var result = results.query;
    
            if (result.rowCount === 0) {
                return callback();
            }
    
            var row = result.rows[0];
    
            console.log(row);
    
            passport.serializeUser(function(res, done) {
                done(null, res);
            });
    
            passport.deserializeUser(function(user, done) {
                done(null, res);
            }); 
    
            callback(null, row);
        }
    
        // Let async handle the order. Allows remixing.
        async.auto({
            connect: [connect],
            query: ['connect', query],
            row: ['query', handleQueryResult]
        }, function (err, results) {
            if (err) {
                return done(err);
            }
    
            // Callback with the row.
            done(null, results.row);
        });
    }
    

    I've used async.auto here, even if async.waterfall would do. The reasoning behind that is that it's difficult to move, add or remove steps in a waterfall, and that's been a source of bugs. The auto lets you add steps without worrying and the order/parallelism is handled by async.

    This is obviously using a lot more vertical space, but I think that's a small price to pay for the modularity.

提交回复
热议问题