回调函数的理解
function add(x,y,callback){
console.log(1)
setTimeout(function(){
var ret = x+y;
// return ret
callback(ret)
},1000)
}
console.log(add(10,20,function(ret){
console.log(ret)
}))
//返回结果
//1
//undefined
//30
- 要分析上面的结果首先我们需要对JavaScript的执行机制有一定的了解
- JavaScript是一门单线程语言,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
1、同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
2、当Event Table中指定的事情完成时,会将这个函数移入Event Queue。
3、主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
4、上述过程会不断重复,也就是常说的Event Loop(事件循环)。
5、我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。
- JavaScript是一门单线程语言,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
- 有了上面的对JavaScript的执行机制的理解现在我们回过头来看程序
所以此处理解为,回调函数(callback)因为setTimeout的存在变成了一个异步执行的函数,需要等到主线程中的任务执行完后才从Event Queue中调出执行。
为什么这么说呢?看如下代码
function add(x,y,callback){
console.log(1)
var ret = x+y;
// return ret
callback(ret)
}
console.log(add(10,20,function(ret){
console.log(ret)
}))
//执行结果
//1
//30
//undefined
也就是说,要有响应的异步请求和回调函数绑定才能发挥它的用处
可能说得有点懵,下面不妨通过一个实例来说明一下
//在router.js中
//编辑学生
var Students = require('./student')
var router = express.Router()
router.get('/students/edit',function(req,res){
Students.editById(req.query.id,function(err,stu){
if(err){
res.status(500).send('Server no connected')
}
res.render('edit.html',{
students:stu
})
})
})
//在student.js中
// 编辑学生
exports.editById = function(student,callback){
fs.readFile(dbPath,'UTF-8',function(err,data){
if(err){
return callback(err)
}
var students = JSON.parse(data).stud
var stu = students.find(function(item){
return item.id ==student
})
callback(null,stu);
})
}
- 这里例子中用了两次异步请求,说到这里可以回头品味一下这段话
- JavaScript是一门单线程语言,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。
JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。
- JavaScript是一门单线程语言,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
- 因此这里的回调函数就是把IO设备的结果返回