phenomLi / Blog

Comments, Thoughts, Conclusions, Ideas, and the progress.
219 stars 17 forks source link

XMLHttpRequest的五个阶段 #14

Open phenomLi opened 6 years ago

phenomLi commented 6 years ago

我们在使用ajax的时候,出于方便,通常使用jquery的$.ajax()方法,简单快捷,帮助我们屏蔽了许多底层的细节。久而久之我们很容易就会遗忘掉原生ajax的一些知识点,比如XMLHttpRequest。下面我就帮助大家对ajax的工作流程梳理一下。
XMLHttpRequest是ajax实现的基础,我们常常用它来对服务器发送一个异步请求:

//实例化一个XMLHttpRequest对象
const xhr = new XMLHttpRequest();

//onreadystatechange方法:判断当前请求的进度
xhr.onreadystatechange = function() {

      //当readyState的值为4时获取返回的值
      if(xhr.readyState === 4) {
            console.log(xhr.responseText);
      }
}

//指定http方法和目标主机,建立连接
xhr.open('GET', 'localhost:8080');
//发送请求
xhr.send();


通常,一次http数据请求,要经过

建立连接 ==> 发送请求 ==> 响应请求 ==> 返回数据

4个阶段,所以说XMLHttpRequest在获取返回的数据的时候,一定要在特点的阶段获取,不然肯定是获取不了的,很显然xhr.readyState === 4的时候,是数据返回成功的阶段。那么在其他阶段,xhr.readyState的值又对应什么呢,我们可以查阅一下MSDN:
可以看到,各个readyState对应的阶段为:

同样的,我们可以很容易想到, onreadystatechange 这个方法肯定不是只被调用一次,而是会被调用多次,在什么时候被调用呢?很明显,是在每次readyState的值发生更改的时候。


看到这里,相信大家已经对ajax的整个工作流程有一个大概的轮廓了,但是为了加深理解,我们可以按照上面的思路,造一个假的XMLHttpRequest对象,模拟一下ajax的工作流程。

MyXMLHttpRequest

首先我们要确定我们的MyXMLHttpRequest对象里面有哪些成员变量和方法:


然后就可以一步一步写我们的 MyXMLHttpRequest 类,由于偷懒,我这里就直接放完整代码了:


export default class MyXMLHttpRequest {

      public readyState: number;
      public responseText: string;
      public status: number;

      constructor() {
          //在初始化对象时设置readyState为0
          this.setReadyState(0);

          console.log('MyXMLHttpRequest被实例化,readyState值为: ' + this.readyState);
      }

      /*
      * 设置readyState的方法
      * @param newState<number> 新的readyState的值
      * @return <void>
      */
      setReadyState(newState: number): void {
          this.readyState = newState;

          //每次readyState发生改变都会调用onreadystatechange方法
          this.onreadystatechange && this.onreadystatechange();
      }

      /*
      * onreadystatechange方法
      */
      onreadystatechange(): void {}

      /*
      * open方法
      * @param httpMethod<string> http请求方式
      * @param targetHost<string> 请求的目标主机
      * @return <void>
      */
      open(httpMethod: string, targethost: string): void {
          //请求开启,open方法被调用,readyState设置为1
          this.setReadyState(1);
      }

      /*
      * send方法
      */
      send(): void {
          //send方法被调用,readyState设置为2
          this.setReadyState(2);

          //模拟响应请求
          setTimeout(() => {
              //响应成功,readyState设置为3
              this.setReadyState(3);
              //模拟接收返回的数据
              setTimeout(() => {
                  //http状态码为200,表示请求没有错误
                  this.status = 200;

                  //responseText的数据也已经准备完成
                  this.responseText = 'Phenom';

                  //所有工作已经完成,readyState设置为4
                  this.setReadyState(4);
              }, 200);
          }, 100);
      }
}


由于客观原因限制,我们没有办法用浏览器环境下的javascript来真实地进行http请求的发送和接收,所以这段代码是不能真正向服务器发送请求的,但是没关系,那不是重点, 我们主要目的是要搞清楚XMLHttpRequest在每个阶段做了什么工作。 由于XMLHttpRequest发送请求是异步的,我们可以用setTimeout来模拟异步的请求流程。


最后我们像普通的ajax操作一样,写我们的代码:

const myXhr = new MyXMLHttpRequest();

myXhr.onreadystatechange = function() {

    console.log('当前的readyState值为: ' + myXhr.readyState);

    if(myXhr.readyState === 4) {
        console.log('responseText的值为: ' + myXhr.responseText);
    }
};

myXhr.open('GET', 'localhost:8080');
myXhr.send();


运行结果: perfect!



总结一下

最后总结一下,XMLHttpRequst的工作流程为: