一、回调函数的定义
回调函数,就是在某个函数执行完毕后,需要执行的函数。它是一种异步编程的解决方案。
function mainFunc(callback) {
// main function codes...
callback();
}
function callbackFunc() {
// callback function codes...
}
mainFunc(callbackFunc);
上面的代码中,mainFunc是主函数,它执行完毕后会执行传入的回调函数callbackFunc。
二、回调函数的应用场景
回调函数应用广泛,常见的场景有:
1、事件回调
在事件处理函数中,可以调用回调函数。
document.addEventListener('click', function() {
// callback function codes...
});
2、异步调用回调
在异步调用中,比如Ajax请求,为了避免程序阻塞,可以使用回调函数。
function ajax(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(xhr.response);
}
};
xhr.open('GET', url, true);
xhr.send();
}
function ajaxCallback(response) {
// callback function codes...
}
ajax('https://www.example.com/api', ajaxCallback);
3、函数式编程中的回调
在函数式编程中,回调函数被广泛应用。
[1, 2, 3, 4].forEach(function(item) {
// callback function codes...
});
三、回调函数与闭包
在回调函数中,闭包也经常用到,它用来避免变量污染。
function outerFunc() {
var outerVar = 'hello';
function innerFunc() {
var innerVar = 'world';
return function() {
console.log(outerVar + innerVar);
};
}
return innerFunc();
}
var callbackFunc = outerFunc();
callbackFunc(); // 'hello world'
在上面的代码中,innerFunc返回一个函数,在返回的函数中需要用到innerVar和outerVar这两个变量,但因为innerVar和outerVar是在innerFunc中定义,所以需要使用闭包将其保存了下来。
四、回调地狱问题
回调函数与异步编程密不可分,但在多个异步调用过程中嵌套使用回调函数会导致代码结构不清晰,出现回调地狱问题。
setTimeout(function() {
// callback1 codes...
setTimeout(function() {
// callback2 codes...
setTimeout(function() {
// callback3 codes...
}, 1000);
}, 2000);
}, 3000);
回调地狱问题解决方案有Promise、async/await等。
function wait(time) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, time);
});
}
async function asyncFunc() {
await wait(3000);
// callback1 codes...
await wait(2000);
// callback2 codes...
await wait(1000);
// callback3 codes...
}
asyncFunc();
五、回调函数的优化
1、函数节流和函数防抖
当回调函数需要频繁触发时,可以通过函数节流和函数防抖等技术对其进行优化。
函数节流
函数节流的作用就是让函数每隔一段时间执行一次,避免函数频繁执行导致性能问题。
function throttle(callback, time) {
var timer;
return function() {
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
callback.apply(this, args);
timer = null;
}, time);
}
};
}
function onScroll() {
// callback codes...
}
window.addEventListener('scroll', throttle(onScroll, 300));
函数防抖
函数防抖的作用是在函数连续触发时,只执行最后一次,并且可以设定触发的时间间隔。
function debounce(callback, time) {
var timer;
return function() {
clearTimeout(timer);
var args = arguments;
timer = setTimeout(function() {
callback.apply(this, args);
}, time);
};
}
function onInputChange() {
// callback codes...
}
var inputEle = document.querySelector('#input');
inputEle.addEventListener('input', debounce(onInputChange, 500));
2、Promise.all和Promise.race
Promise.all可以将多个Promise实例包装成一个新的Promise实例,当所有的Promise实例都resolve时,它才resolve,当一个Promise实例reject时,它就reject。
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(2);
var p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function(results) {
console.log(results); // [1, 2, 3]
});
Promise.race与Promise.all类似,它只要有一个Promise实例resolve或reject就resolve或reject。
var p1 = new Promise(function(resolve) {
setTimeout(function() {
resolve('hello');
}, 2000);
});
var p2 = new Promise(function(resolve) {
setTimeout(function() {
resolve('world');
}, 1000);
});
Promise.race([p1, p2]).then(function(result) {
console.log(result); // 'world'
});
六、总结
回调函数是异步编程中的重要概念,其应用广泛。回调函数与闭包、异步调用等概念结合使用,可以实现代码逻辑的清晰。同时,针对回调地狱问题和回调函数需要频繁触发的情况,可以采用函数节流、函数防抖等优化方案。而Promise.all、Promise.race等API则为处理多个Promise实例时带来了更高的便捷性。

