先看一段代码
function f2() {
console.log(2)
}
function f1(callback){
callback()
console.log(1)
}
f1(f2)
//2 1 3
function f2() {
console.log(2)
}
function f1(callback){
setTimeout(callback,0)
console.log(1)
}
f1(f2)
//1 3 2
- 上面代码有何区别呢,第一段代码f1函数返回之前callback会被立即执行
第二段代码则是f1函数返回之后要等任务完成之后才调用callback。
这里的setTimeout将callback加入任务队列 - 跟java中的异步和同步代码会交叉输出相比,js中的异步其实是排好队输出的。由于js是单线程执行代码的,所以没有那种交叉输出的效果。
- setTimeout的回调的执行顺序不仅与代码顺序有关还和延迟时间有关。
f1();
f2();
f3();
如果f1中执行了大量的耗时操作,而且f2需要在f1之后执行。则程序可以改为回调的形式。如下:
function f1(callback){
setTimeout(function () {
// f1的大量耗时任务代码并的到三个结果i,l,you.
console.log("this is function1");
var i = "i", l = "love", y = "you";
if (callback && typeof(callback) === "function") {
callback(i,l,y);
}
}, 50);
}
function f2(a, b, c) {
alert(a + " " + b + " " + c);
console.log("this is function2");
}
function f3(){console.log("this is function3");}
f1(f2);
f3();
运行结果:
this is function3
this is function1
i love you
this is function2
- 采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。回调函数的优点是简单,轻量级(不需要额外的库)。缺点是各个部分之间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。某个操作需要经过多个非阻塞的IO操作,每一个结果都是通过回调,产生意大利面条式(spaghetti)的代码。
- Promises的本质实际就是通过状态机来实现的
setTimeout(()=>console.log(4),0)
var p1=new Promise(function(resolve,reject){
setTimeout(()=> console.log(3),0)
resolve('hello')
}).then(function(val){
console.log(val)
})
setTimeout(()=> console.log(2),0)
console.log(5)
//5 hello 4 3 2