JS- Ajax原理与Callback Hell

252 阅读3分钟

JS中同步和异步

同步和异步的执行顺序

Snipaste_2023-05-17_10-43-38.png

// 伪代码

setTimeout(() => {
    task()       // 表示一个任务
},2000)

sleep(5000)      // 表示一个很复杂的同步任务

定时器中的2秒后执行并不一定是2秒后就一定执行,由于sleep是同步任务同时又由于JS是单线程的,因此必须等sleep结果出来了,JS中主线程处于空闲状态才去执行事件队列Event Queus中的异步结果(定时器会先到Event Table,等定时器的时间到了就会进入Event Queue)

经典面试题

setTimeout({},0)是在什么时候执行

=> 虽然写的是0,但是并不是立即执行,最少也要等到4ms之后才能被执行

// 下面代码的执行结果?
console.log(1)
setTimeout(() => {
    console.log(2)
},0)
console.log(3)

执行结果是: 1 3 2

代码自上而下执行,当读到定时器时先放入Event Table等主线程中同步任务(console.log(1)和console.log(3))执行完成开始空闲下来时才会拿到Event Queue中的异步结果(当定时器时间到了,会进入Event Queue中返回结果)

Ajax

定义

Ajax 本身不是一种技术,而是一种将一些现有技术结合起来使用的方法,AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML),AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

原理

如何用原生的JS代码实现Ajax?

// 1. 创建XMLHttpRequest对象
var xtmlhttp
var url = 'http://jsonplaceholder.typicode.com/users'
if(window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest()
// 兼容早期浏览器
} else {
    xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
}
// 2. 发送请求
xmlhttp.open('GET',url,true)
xmlhttp.send()
// 3. 接收服务端相应
xmlhttp.onreadystatechange = function() {
    if(xmlhttp.readyState === 4 && xmlhttp.status === 200){
        var obj = JSON.parse(xmlhttp.responseText)
        console.log(obj);
    }
}

image.png

将Ajax封装成可复用的函数

function ajax(url,callback) {
    // 1. 创建XMLHttpRequest对象
    var xtmlhttp
    if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
    // 兼容早期浏览器
    } else {
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
    }
    // 2. 发送请求
    xmlhttp.open('GET',url,true)
    xmlhttp.send()
    // 3. 接收服务端相应
    xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200){
            var obj = JSON.parse(xmlhttp.responseText)
            callback(obj);
        }
    }
    
}

var url = 'http://jsonplaceholder.typicode.com/users'
ajax(url,res => console.log(res))

Callback hell(又叫“回调深渊”或者"回调地狱")

eg: 假如有3个Ajax请求,这3个Ajax是有相互依赖关系,即第一个请求执行结束之后,再 执行第二个请求,第二个请求结束后再执行第三个请求

1 -> 2 -> 3

Ajax是一个异步操作,那我们怎么知道当前1的任务在什么时候完成呢?

前面封装的ajax函数中,传了一个参数callback,如果它被调用就说明ajax请求结束了

因此,我们可以在1的回调函数callback中发送2的ajax请求,然后再在2的回调函数中发送3的ajax请求

eg: 在static目录下新建a.json、b.json、c.json,请求到a.json的结果后再请求b的结果再请求c.json的结果

Snipaste_2023-05-17_13-35-05.png

function ajax(url,callback) {
    // 1. 创建XMLHttpRequest对象
    var xtmlhttp
    if(window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
    // 兼容早期浏览器
    } else {
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
    }
    // 2. 发送请求
    xmlhttp.open('GET',url,true)
    xmlhttp.send()
    // 3. 接收服务端相应
    xmlhttp.onreadystatechange = function() {
        if(xmlhttp.readyState === 4 && xmlhttp.status === 200){
            var obj = JSON.parse(xmlhttp.responseText)
            callback(obj);
        }
    }
    
}
// 1 -> 2 -> 3
ajax('static/a.json',res =>{
    console.log(res)           // {a: '我是A'}
    ajax('static/b.json',res => {
        console.log(res);     // {b: '我是B'}
        ajax('static/c.json',res => {
            console.log(res); // {c: '我是C'}
        })
    })
})

image.png

这种应用场景还是非常常见的,eg: 省市区三级联动,每个下面的层级都依靠上一层级的结果,产生一定的依赖关系

上面这种层层嵌套异步的ajax请求,每一层都都产生一定的依赖关系,这种就叫callback hell,这种结构对于代码的可读性或者维护都不是很友好