异步、Ajax、回调函数、Promise
异步
在JS中异步大致分为三类代码
- Ajax
- addEventListener
- setTimerout()
异步代码会在将来的某一时刻被执行,在这期间浏览器可以去执行其他的代码。
异步的出现是有必要的,想象一下ajax请求如果变成同步执行,那么浏览器就会因为等待一个请求而停止其他程序的执行。在这种情况下点击浏览器不会得到任何反馈。
所以永远都不要使用XMLHttpRequest类中的第二个参数(用于设置ajax请求是否使用异步)。
Ajax
Asynchronous JavaScript + XML(异步JavaScript和XML), 其本身不是一种新技术,而是一个在 2005年被Jesse James Garrett提出的新术语。主要作用是在不刷新浏览器的情况下先服务器发出请求。
四个步骤
XMLHttpRequest
API就是Ajax的核心。使用Ajax完成一次请求只有一下四个步骤:
1.XMLHttpRequest类创建一个请求。 2.调用open方法打开一个请求。 3.监听onreadystatechange事件,该事件用于监听从一个请求到响应的生命周期。 4.调用send方法发送请求
简化一下就是
- 创建请求
- 打开请求
- 监听响应
- 发送请求
代码表示如下:
const request = XMLHttpRequest();
request.open("GET","baidu.com");
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status <400){
//success
}else{
//error
}
}
}
request.send();
XMLHttpRequest.readyState 生命周期状态码
request.status 响应的状态码
回调函数
如何理解回调?
回调函数遵循以下两条原则:
- 写出来的方法不是给自己调用,而是给别人(这里指的别人可以指的是其他函数)。
- 方法会在将来的某个时间被其他人执行,而不是开发人员自己手动调用执行。
只要不是自己直接调用的方法都算是将来执行,例如
fn()
表示我现在立即执行了fn()
函数。
只要符合以上两条规则就说明该函数是回调函数。
回调函数: 回头我再调用,不是现在调用。
回调函数常常用于处理异步
举个栗子
异步代码中的回调函数
fn(){
console.log('hi');
}
setTimeout(fn,1000);
代码中我负责声明了一个fn函数,可以看到我只是声明了并没有去调用。而是setTimeout
在经过1000毫秒之后调用了fn
函数.那么就符合上面说的两条规则
原生js的foreach循环和jQuery中的each循环
let array = [12,33,4,12,5,1];
//声明函数
function foreach(fn){
for(let i=0;i<this.length;i++){
fn(this[i],i,this);
}
}
//调用函数
foreach.call(array,console.log)
可以看到foreach中的fn我并没有去调用它这个而是foreach自己调用的,我只不过是声明了一个匿名函数传进去。这个函数是console对象下的log方法。函数在将来被执行(这里的将来就是foreach自己的过程中被执行)。
如果将Array原型上的foreach方法该位上面的foreach方法是完全可以使用的。
还可以说明一个问题,为什么foreach中return 不能直接阻止foreach函数的执行。
其实return确实中断了一个函数只不过是foreach中的回调函数fn。return之后返回到foreach函数,foreach函数中的for循环还是会照常执行。所以return只能达到continue的效果。
Promise
Promise规范了异步代码的处理方案,与Ajax一样Promise并不是一门新技术。
js在Promise没有出现之前,使用回调函数是处理异步代码的首选方案,也没有一个统一的处理回调的方法。
例如ajax封装就可以有许多种写发,有的人用success和error表示成功和错误;有的人使用success和fail表示成功和失败的回调函数。有些人参数的是对象(对象里面包含了回调函数),有的人参数直接是回调函数。完全没有一个统一的规范。
使用Promise封装Ajax
function ajax(method,url,data){
return new Promise((resolve,reject)=>{
const request = XMLHttpRequest();
request.open(method,url);
request.onreadystatechange = ()=>{
if(request.readyState === 4){
if(request.status >= 200 && request.status <400){
resolve(request.response);
}else{
reject()
}
}
request.send(data);
}
})
这样就可以统一了Promise对异步代码处理方式
ajax("GET",baidu.com)
.then((data)=>{})
.catch((err)=>{})
Promise.all
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都完成(resolved)或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // [3,43,'foo']
});
Promise.race
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
相当于数组中的所有promise同时执行,谁最快就返回谁的结果
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value); // 'two'
});
转载自:https://juejin.cn/post/6858886304181649415