先弄明白一个概念,什么是js的异步操作。
我们平时有意无意写的代码的,大部分都是同步执行的,就是从上往下依次执行。同一时间只做一件事,这个事情做完了,再做下一个事儿。
可以想象一下做核酸检测,如果只有一个人做,大家都排着队,你做完了下个做。这个就是同步的概念。同步的好处就是简单好维护,同步操作的缺点就是会阻塞后面代码的执行。
想象一下,排队前面的一个人网络有问题,又不肯走,在那里一遍一遍刷新,后面的是不是都不能去做呢?
在前端开发中也是如此。在js中,如果当前执行的任务需要花费比较久,那么后面的程序就只能一直等待。从前端页面来看,有可能会造成页面渲染的阻塞,大大影响用户体验。
所以就有了异步的概念。
异步,指的是当前代码的执行不影响后面代码的执行。当程序运行到异步的代码时,会将该异步的代码作为任务放进任务队列,而不是推入主线程的调用栈。等主线程执行完之后,再去任务队列里执行对应的任务即可。因此,异步操作的优点就是:不会阻塞后续代码的执行。
回到核酸检测,当有一个人核酸码出问题,我们把他请到屋子里,吹着空调,让他等着,等后面的长队都做完了(主线程执行完),再问他好了没有,好的话就跟着下一批一起做。
这就是异步最简单的理解。
对于setTimeout、setInterval、addEventListener这种异步场景,不需要我们手动实现异步,浏览器或者js底层已经帮我们实现了,直接调用即可。
但是常见的ajax请求,就需要我们来实现了。
方法一:回调函数
在对异步的监听过程中,是js自动去执行的,这个不用我们考虑,他像是非常贴心负责的老师,隔一段时间就去看主任务执行完没,执行完了就让异步队列里面的方法去执行,这个方法就是回调函数,需要我们自己去定义。
let xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.baidu.com',true);
xhr.send();
xhr.onreadystatechange = function(){
if (xhr.readyState === 4 && xhr.status === 200){
let result = JSON.parse(xhr.responseText);
// 这里的操作就是回调函数去出俩得到的result结果
callback(result)
}
}
回调函数中执行的是我们请求成功之后要做的下一步操作。这样就初步实现了异步,但是回调函数有一个非常严重的缺点,那就是回调地狱的问题。回调地狱就是回调里面还有请求的话,层级太深,不利于维护。
方法二:promise
const promise = new Promise((resolve, reject) => {
// 一段耗时的异步操作
resolve('a');
});
promise.then(result => console.log(result))
异步操作以后,成功的执行resolve,失败的执行reject,我们的回调写在then里面
可以简单理解为把层层嵌套的回调函数变成了链式调用,通过then执行的就是回调函数。
方法三:async/await
这是ES7提出的关于异步的终极解决方案,就是最好的方法。
它其实就是primise的语法糖,使用起来更直观,更容易操作。async和await是配合使用的。两个必须配套出现。async 嵌套await, await后面就是回调函数。
async function getResult(){
let res = await ajax()
console.log(res)
}
这种方式让我们用同步的写法去写异步,感官上更加的舒适。
以上就是异步操作的三个解决方案。面试遇到了不要慌张。