likes
comments
collection
share

异步、Ajax、回调函数、Promise

作者站长头像
站长
· 阅读数 0

异步

在JS中异步大致分为三类代码

  1. Ajax
  2. addEventListener
  3. setTimerout()

异步代码会在将来的某一时刻被执行,在这期间浏览器可以去执行其他的代码。

异步的出现是有必要的,想象一下ajax请求如果变成同步执行,那么浏览器就会因为等待一个请求而停止其他程序的执行。在这种情况下点击浏览器不会得到任何反馈。

所以永远都不要使用XMLHttpRequest类中的第二个参数(用于设置ajax请求是否使用异步)。

Ajax

Asynchronous JavaScript + XML(异步JavaScript和XML), 其本身不是一种新技术,而是一个在 2005年被Jesse James Garrett提出的新术语。主要作用是在不刷新浏览器的情况下先服务器发出请求。

四个步骤

XMLHttpRequestAPI就是Ajax的核心。使用Ajax完成一次请求只有一下四个步骤:

1.XMLHttpRequest类创建一个请求。 2.调用open方法打开一个请求。 3.监听onreadystatechange事件,该事件用于监听从一个请求到响应的生命周期。 4.调用send方法发送请求

简化一下就是

  1. 创建请求
  2. 打开请求
  3. 监听响应
  4. 发送请求

代码表示如下:

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 生命周期状态码 异步、Ajax、回调函数、Promise

request.status 响应的状态码

回调函数

如何理解回调?

回调函数遵循以下两条原则:

  1. 写出来的方法不是给自己调用,而是给别人(这里指的别人可以指的是其他函数)。
  2. 方法会在将来的某个时间被其他人执行,而不是开发人员自己手动调用执行。

只要不是自己直接调用的方法都算是将来执行,例如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'
});