callback 函数的 scope 变数范围问题
发布于 6个月前 作者 grass0916 501 次浏览

在 User_UidSearch 函式当中我想要取用上一层 for 回圈的i ,但没有办法,这个 i 的scope 让我有点疑惑,不晓得各位前辈有没有不使用全域变数的解决办法呢?

Task_TidSearchExecution = function(tid, callback) {
    var sql = "SELECT * FROM execution WHERE task = '" + tid + "'";
    dbclient.query(sql, function (err, results) {
        if (err || results.length <= 0)
            callback(false);
        else {
            console.log(results.length);
            for (var i = 0 ; i < results.length ; i++) {
                User_UidSearch(results[i].employee, function (user) {
                    console.log(i);
                    // results[i]['email'] = user.email;
                });
            }
            
            callback(results);
        }
    });
}

console.log(result[i]); 输出都是 undefined。 如果我的 results.length 为 2,console.log(i); 都是 2

17 回复

经典问题。

for (var i = 0 ; i < results.length ; i++) {
  (function (j) {
    User_UidSearch(results[j].employee, function (user) {
        console.log(j);
        // results[j]['email'] = user.email;
    });
  })(i);
};


主要思路就是,把 i 变成局部变量,而不是引用,就好了。

@alsotang 感谢大侠帮助,成功的让 i 进去该 callback 中。 我想应该是我javascript 学艺不精,请问这个方法有什么名称吗?

@chloe 谢谢前辈。 现在衍生了其他后续问题,待我修正后再一次把问题的解法程式码更新到文章中。

经过前辈们指教,解决的上述的问题,以及衍生出来的问题。

程式码如下,不过觉得可读性有点低,想请教有没有更精炼的优化方式。


Task_TidSearchExecution = function(tid, callback) {
    var sql = "SELECT * FROM execution WHERE task = '" + tid + "'";
    dbclient.query(sql, function (err, results) {
        if (err || results.length <= 0)
            callback(false);
        else {
            AddEmployeesInfo(function (results) {
                console.log(results);
                callback(results);
            });

            function AddEmployeesInfo(callback) {
                for (var i = 0 ; i < results.length ; i++) {
                    (function (j) {
                        User_UidSearch(results[j].employee, function (user) {
                            results[j]['uid'] = user.uid;
                            results[j]['email'] = user.email;
                            if (j == results.length-1)
                                callback(results);
                        });
                    })(i);
                }
            };
        }
    });
}

@chloe @alsotang 下面回覆附上了成功运行的程式码,想问问有没有其他精炼的写法。

看这术语用的你是台湾人吗?

@grass0916

我有几个建议。

对于 Task_TidSearchExecution = function(tid, callback)

  • 改成var Task_TidSearchExecution = function(tid, callback)
  • 函数的命名风格保持一致,参照下面的AddEmployeesInfo,把Task_TidSearchExecution改为findExecutionTaskById更自然点;
  • 这里的函数参数callback没有遵循Node.js的例行写法。一般来说,callback函数都被定义成callback(err, arg1, arg2, ...)的形式。因此,建议把callback的函数原型修改为callback(err, results)
  • 函数体里,if (err || results.length<=0)中没必要添加results.length<=0的判断;
  • else部分,AddEmployeesInfo函数的定义似乎是多此一举,直接在else下面写for循环的语句就可以了;
  • 不了解数据库表和User_UidSearch函数的定义,更多优化无从下手。感觉有进一步优化的空间。

@ravenwang 我都没发现这点,经你一提醒,看了看还真是台湾术语

@alsotang 大家不会排斥我吧 … ? Q_Q

@bnuhero 前辈早安,根据你宝贵的建议我尝试做了一些更动,还请你审视一番。

  1. “Task_” 系列函数是 SQL query 用途,所以他们本身是属最外层,所以我就没有多留意加上var,虽然不影响结果但想起曾经看过一篇文章「变数通通都加上var」这类的观念。
  2. 对,其实一致也会比较好,那我就更改了。
  3. 这点其实我不太清楚,麻烦你再帮忙解释了 ><
  4. 由于 query 的结果可能会有程式码执行成功但无资料回传,所以仍必须写上 length == 0,不过我改了一下写法,可以看看文后的程式码。
  5. 逻辑问题,抱歉还不是很熟callback,我修正了@@
  6. 于后面附上
/* =============================================
          Task - Employer maintain tasks
   ============================================= */

app.get('/maintain_task', function (req, res) {
    // Users can see this page when them logged-in.
    if (req.session) {
        var user = { "uid": req.session.uid, "email": req.session.email };
        var query_tid = req.query.tid;

        findEmployerByTid(query_tid, function (manager) {
            // If this task manager matches on user's id.
            if (manager !== false && user.uid === manager) {
                // Get the task info.
                findTaskByTid(query_tid, function (task) {
                    // Get these executions info with employees' info.
                    findExecutionTaskByTid(task.tid, function (employees) {
                        res.render("task_system/maintain_task", {
                            layout: false,
                            pagename: "Page Name",
                            user: user,
                            task: task,
                            employees: employees,
                            errormessage: "null"
                        });
                    });
                });
            }
            // If this task manager no matches on user's id.
            else
                res.redirect('/employer_task_list');
        });
    }
    // Users must log in first.
    else
        res.redirect('/login');
});

var findExecutionTaskByTid = function(tid, callback) {
    var sql = "SELECT * FROM execution WHERE task = '" + tid + "'";
    dbclient.query(sql, function (err, results) {
        if (err)
            callback(false);
        else if (results.length == 0)
            callback(new Array());

        for (var i = 0 ; i < results.length ; i++) {
            (function (j) {
                findUserByUid(results[j].employee, function (user) {
                    results[j]['uid'] = user.uid;
                    results[j]['email'] = user.email;
                    if (j == results.length-1)
                        callback(results);
                });
            })(i);
        }
    });
}

var findUserByUid = function (uid, callback) {
    var sql = "SELECT * FROM user WHERE uid = '" + uid + "'";
    dbclient.query(sql, function (err, results) {
        if (err || results.length <= 0)
            callback(false);
        else
            callback(results[0]);
    });
}

var findTaskByTid = function (tid, callback) {
    var sql = "SELECT * FROM task WHERE tid = '" + tid +"'";
    dbclient.query(sql, function (err, results) {
        if (err || results.length <= 0)
            callback(false);
        else
            callback(results[0]);
    });
}

var findEmployerByTid = function (tid, callback) {
    var sql = "SELECT employer FROM task WHERE tid = '" + tid +"'";
    dbclient.query(sql, function (err, results) {
        if (err || results.length <= 0)
            callback(false);
        else
            callback(results[0].employer);
    });
}

SQL

@grass0916 那倒不会…只是我平时很少见到那些翻译

@grass0916 欢迎台湾同胞:)

@ravenwang @alsotang 谢谢大家不嫌弃 <(_ _ *)>

@grass0916

关于回调函数的惯常写法,举个例子。

// 通常回调函数的第一个参数是一个Error对象。
var printNumber = function(err, num) {
  if (err) {
    console.error(err);
  } else {
    console.log(num);
 };
};

// 主函数
var getNumber = function(callback) {
  // 从数据库中读取数据

  // 如果读取过程中出错了,
     callback(new Error('Something is wrong'));
  // 否则,假设读到的数据保存在num变量中,
     callback(null, num);
};

// 调用主函数
getNumber(printNumber);

像你的代码中的callback,把代表出错信息的对象和正常结果的对象用同一个参数表示,不符合如上所述的惯常用法。

回到顶部