异步无处不在:回调函数(二)
(。・∀・)ノ゙嗨,我是你稳定更新、从不断更的勾勾。
上周五,我们聊了 JS 中的同步模式和异步模式。本篇,我们来唠唠回调函数。
异步代码的执行流程如下:
通过上图,我们会看到,在整个代码的执行中,JS 本身的执行依然是单线程的,异步执行的最终结果,依然需要回到 JS 线程上进行处理。
在 JS 中,异步的结果回到 JS 主线程所采用的是 “ 回调函数 ” 的形式。
所谓的回调函数就是在 JS 主线程上声明一个函数,然后将函数作为参数传入异步调用线程,当异步执行结束后,调用这个函数,将结果以实参的形式传入函数的调用(也有可能不传参,但是函数调用一定会有)。
上篇的代码中 setTimeout 就是一个异步方法,传入的第一个参数就是回调函数,这个函数的执行就是消息队列中的 “回调”。
下面我们自己封装一个 ajax 请求,来进一步说明回调函数与异步的关系
Ajax 的异步请求封装
function myAjax(url,callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// 成功的回调
callback(null,this.responseText)
} else {
// 失败的回调
callback(new Error(),null);
}
}
}
xhr.open('get', url)
xhr.send();
}
上面的代码,封装了一个 myAjax 的函数,用于发送异步的 ajax 请求。
函数调用时,代码实际是按照同步模式执行的。当执行到 xhr.send() 时,就会开启异步的网络请求,向指定的 url 地址发送。从建立网络连接到断开网络连接的整个过程是异步线程在执行的。
换个说法就是 myAjax 函数执行到 xhr.send() 后,函数的调用执行就已经结束了。如果 myAjax 函数调用的后面有代码,则会继续执行,不会等待 ajax 的请求结果。
但是,myAjax 函数调用结束后,ajax 的网络请求却依然在进行着。
如果想要获取到 ajax 网络请求的结果,我们就需要在结果返回后,调用一个 JS 线程的函数,将结果以实参的形式传入:
myAjax('./d1.json',function(err,data){
console.log(data);
})
回调函数让我们轻松处理异步的结果。但是,如果代码是异步执行的,而逻辑是同步的,就会出现 “回调地狱”,举个栗子:
代码 B 需要等待代码 A 执行结束才能执行,而代码 C 又需要等待代码 B,代码 D 又需要等待代码 C,而代码 A、B、C 都是异步执行的。
// 回调函数 回调地狱
myAjax('./d1.json',function(err,data){
console.log(data);
if(!err){
myAjax('./d2.json',function(err,data){
console.log(data);
if(!err){
myAjax('./d3.json',function(){
console.log(data);
})
}
})
}
})
没错,代码执行是异步的。但是异步的结果,是需要有强前后顺序的,著名的"回调地狱"就是这么诞生的。
相对来说,代码逻辑是固定的,但是编码体验要差很多,尤其在后期维护的时候,层级嵌套太深,让人头皮发麻。
如何让我们的代码不在地狱中受苦呢?
下篇告诉你(ง •_•)ง。
推荐阅读:
前端人因为 Vue3 的 Ref-sugar 提案打起来了!
点点“赞”和“在看”,保护头发,减少bug。