callback 函数的 scope 变数范围问题
发布于 10 年前 作者 grass0916 4600 次浏览 最后一次编辑是 8 年前

在 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,把代表出错信息的对象和正常结果的对象用同一个参数表示,不符合如上所述的惯常用法。

回到顶部