JavaScript同步回调和异步回调
- 一、定义
- 二、同步回调
- 三、异步回调
- 四、异步回调函数和同步回调函数区分
- 五、回调函数使用场景
一、定义
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed
就是将一个函数作为参数传递给另一个函数,作为参数的这个函数就是回调函数。
- 在javascript中函数是一个对象,准确的来说函数是用function()构造函数创建的一个function对象,因此我们可以将函数存储在变量中,当然也就可以将存储在变量中的函数作为一个参数传递给另一个函数,这就是回调函数。
function f1(tag2){
tag2=tag2+1;
console.log("callback:"+tag2)
}
function f2(tag1,cb){
var tag2=tag1;
cb(tag2)
console.log("main:"+tag2)
}
f2(1,f1)
// callback:2
// parent:1
将f1作为参数传递给了f2()函数,这时在函数f2()中f1就是回调函数。
二、同步回调
- 异步回调函数的确是应该在函数的最后执行,不过上面的例子是一个同步回调函数,函数的执行顺序依然自上而下顺序执行。(因为f2是同步函数,具体后面再说)
- 例如如下也是同步回调
function f2(tag1,cb){
var tag2=tag1+1;
console.log("main:"+tag2)
return cb(tag2)
}
f2(1,function (tag2){
tag2=tag2+1;
console.log("callback:"+tag2)
})
// main:2
// callback:3
function f1(tag2){
tag2=tag2+1;
console.log("callback:"+tag2)
}
function f2(tag1,cb){
var tag2=tag1+1;
console.log("main:"+tag2)
return cb(tag2)
}
f2(1,f1)
// main:2
// callback:3
三、异步回调
- 我们把同步操作变成了异步操作,f1(假如为f1异步函数)不会堵塞程序运行,相当于先执行程序的主要逻辑代码(主要逻辑代码指的是:比如例子2中的console.log(“程序执行结束!”)),将耗时的操作推迟执行(耗时操作即f1)。
- 例子1
function f2() {
console.log('f2 finished')
}
function f1(cb) {
setTimeout(cb,1000) //用setTimeout()模拟耗时操作
console.log('f1 finished')
}
f1(f2); //得到的结果是 f1 finished ,f2 finished
这里我们用setTimeout()来模拟耗时操作,前提是js中的setTimeout()函数支持异步处理,所以我们得到的结果是 f1 finished ,f2 finished
- 例子2
var fs = require("fs");
fs.readFile('input.txt','utf-8', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执行结束!"); //
// 程序执行结束!
// 我们来测试一下异步回调函数
- 上面例子中我们先创建了一个文件input.txt,里面的内容是:'我们来测试一下异步回调函数’如果按照同步的思维,程序应该执行fs.readFile,直到文件读完之后才执行后面的console.log(“程序执行结束!”); 然而node中的fs.readFile是支持异步处理的,因此程序执行到这儿的时候并不会阻塞,而是继续向后执行,当文件读取完毕之后再自动调用传入的匿名回调函数,因此出现了上面的结果。
- 异步函数fs.readFile()及异步回调函数function (err, data){},不会阻塞后面的代码执行,在此的程序指的是,console.log(“程序执行结束!”)
四、异步回调函数和同步回调函数区分
- 异步函数的回调函数,就是异步回调函数;
- 同步函数的回调函数,就是同步回调函数;
- 回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。
五、回调函数使用场景
- 资源加载:动态加载js文件后执行回调,加载iframe后执行回调,ajax操作回调,图片加载完成执行回调,AJAX等等。
- 事件:DOM事件及Node.js事件基于回调机制 (Node.js回调可能会出现多层回调嵌套的问题)。
- setTimeout的延迟时间为0,这个hack经常被用到,settimeout调用的函数其实就是一个callback的体现
- 链式调用:链式调用的时候,在赋值器(setter)方法中(或者本身没有返回值的方法中)很容易实现链式调用,而取值器(getter)相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是this指针,如果要实现链式方法,可以用回调函数来实现【不太能理解】
- setTimeout、setInterval的函数调用得到其返回值。由于两个函数都是异步的,即:他们的调用时序和程序的主流程是相对独立的,所以没有办法在主体里面等待它们的返回值,它们被打开的时候程序也不会停下来等待,否则也就失去了setTimeout及setInterval的意义了,所以用return已经没有意义,只能使用callback。【不太理解】callback的意义在于将timer执行的结果通知给代理函数进行及时处理。