# 回调函数
# 定义
数组作为头等对象,是可以作为值来传递的,我们把函数作为值传递的函数叫做回调函数。
# 回调函数用来解决什么
这儿涉及到同步异步的概念(看同步异步章节).
主要就是因为异步的问题,js 引擎已经执行完毕,但是数据还没有饭回来,所以我们不得不利用各种技术去管理那段 js 引擎不执行的时间。以便我们最终能够拿到数据。
# 同步回调vs异步回调
异步函数也可以分为同步函数和异步函数,同步就是会阻塞引擎的指向,异步则不会。
- 同步回调
function foo(fn) {
// ... 执行代码
fn();
}
foo(function () {console.log(2)});
console.log(1);
var arr = [1, 2, 3];
arr.forEach(item => {console.log(item)});
异步回调
setTimeout(function () {
console.log(1);
}, 1000);
# 回调地狱??
假如我们有这么一个业务,需要先获取用户的身份证号,然后根据身份证去查用户的成绩,然后根据成绩进行排名。
getsfz(function (sfz) {
getcj(sfz, function (cj) {
getmc(cj, function (paiming) {
console.log('排名', paiming);
});
})
})
这样的代码常被称为“回调地狱", 也被称为“末日金字塔.但是真的就是因为它的丑陋还被人所称为地狱吗?
那么我们也可以这么写。
function getA() {
getsfz(getB)
}
function getB(sfz) {
getcj(sfz, getC);
}
function getC(cj) {
getmc(cj, getD);
}
function getD(paiming) {
console.log('排名', paiming);
}
getA();
这样书写好看多了吧,难道没有形成金字塔状,它就不是地狱了吗?
我么来看看是它如何完成的,我们通调用 A,使得B硬编码与A中,又调用C,使得C硬编码在B中,让D硬编码在C中。
硬编码使得代码变的很脆弱,它不会考虑过程中存在错误,也不考虑是否跳过错误处理流程上。这才是回调地狱的真正原因。
# 信任问题
一开始说过,引擎执行完之后就会停止,因为事件循环的一个编排,使得程序又得到了延续。同步的代码执行的控制权是在我们的手中的,我们可以进行错误的控制,但是回调函数的控制权将会发生倒转。
有一个别人写好的函数,比如我们在做支付,我们需要一个功能在用户支付之后,我们能够通过一个函数来追踪到付款的流向。我们现在借助一个第三方的函数帮助我们完成这个需求。(我们是不知道第三方函数的具体逻辑是怎么样的,这也不是我们应该关心的,我们只需要知道它可以就行)。
我们希望追踪到之后,然后删除这个订单。
function pay() {
// 检测到用户付款了。
// 我们借助函数来追踪一下
zhuizong(function (flag) {
// 假设这个第三方函数是一个异步的(一般都是异步的)。
// 我们需要一个回调,来延迟代码的执行。
// 不管有没有追踪到都会给我门返回一个标识。
if (flag) {
// 追踪到之后, 删除订单。
deleteOrder();
} else {
// 没有追踪到 (我们可能希望重新追踪一下)
zhuizong();
}
});
}
写完之后,确实可以,但是现在这个控制权已经到了第三方函数上了,关键看人家怎么处理,假如它要是没有进行一些判断。那么我们可能就会被咬到。
1. 调用太早。2. 调用太晚。3.或者不调用。
# 拯救回调
我们上面讲,硬编码的不好之一就是它不能够对错误进行处理。 下面这种分离的回调,提供了一个错误一个成功的通知。 (promise 做法)
function success() {};
function fail() {};
ajax(success, fail);
错误优先. Node做法。
function response(err, data) {
if (err) {
throw err;
}
console.log(data);
}
ajax(response);
它只是会解决对错误的一个处理。并没有解决信任问题。假如你信息它,那你还需要对可能同时得到成功和失败的结果,或者什么都得不到的处理。
# 封装一个超时取消事件的工具
function timeout(func, delay) {
var init = setTimeout(function () {
init = null;
fn(new Error('err'));
}, delay);
return function () {
if (init) {
setTimeout(init);
fn.apply(this, [null].concat([].slice.call(arguments)));
}
}
}
function response(err, data) {
if (err) {
throw err;
}
console.log(data);
}
ajax('http://xxx.xxx.com', timeout(response, 1000));
← 页面是如何活动起来的 异步方案 →