JavaScript高级语法之异步(XMLHttpRequest)

目录

1 XMLHttpRequest对象

2 XMLHttpRequest实例的属性

2.1 readyState

2.2 onreadystatechange

2.3 response

2.4 responseType

2.5 responseText

2.6 responseXML

2.7 status

2.8 statusText

2.9 timeout

2.10 ontimeout

2.11 withCredentials(跨域)

3 XMLHttpRequest实例的方法

3.1 abort()

3.2 getAllResponseHeaders()

3.3 getResponseHeader()

3.4 open() 打开路径

3.5 send() 发送参数

3.6 setRequestHeader()

3.7 overrideMimeType()

4 XMLHttpRequest实例的事件

4.1 readyStateChange事件

4.2 progress事件

4.3 load事件、error事件、abort事件

4.4 loadend事件

5 JSONP


Ajax指的是一种JavaScript在浏览器中的使用方法。它通过原生的XMLHttpRequest对象发出HTTP请求,得到服务器返回的数据后,再进行处理。

Ajax可以是同步请求,也可以是异步请求。但是,大多数情况下,特指异步请求。因为同步的Ajax请求,对浏览器有”堵塞效应“。

1 XMLHttpRequest对象

XMLHttpRequest对象用来在浏览器与服务器之间传送数据。它提供了一种方法,使得页面不用全部刷新,就可以从浏览器端获取新的数据。这意味着,通过XMLHttpRequest对象,可以每次只更新网页的一个部分,从而不打断用户正在做的事情。Ajax操作就是基于XMLHttpRequest对象的。

虽然名字里面有XML,但是实际上,XMLHttpRequest可以报送各种数据,包括字符串和二进制,而且除了HTTP,它还支持通过其他协议传送(比如File和FTP)。

下面是XMLHttpRequest对象的典型用法。

// 新建一个XMLHttpRequest实例对象
var xhr = new XMLHttpRequest();

// 指定通信过程中状态改变时的回调函数
xhr.onreadystatechange = function(){
  // 通信成功时,状态值为4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {
  console.error(xhr.statusText);
};

// open方式用于指定HTTP动词、请求的网址、是否异步
xhr.open('GET', '/endpoint', true);

// 发送HTTP请求
xhr.send(null);

下面是发出同步请求的一个例子。

var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
}

2 XMLHttpRequest实例的属性

2.1 readyState

readyState是一个只读属性,用一个整数和对应的常量,表示XMLHttpRequest请求当前所处的状态

  • 0,对应常量UNSENT,表示XMLHttpRequest实例已经生成,但是open()方法还没有被调用。
  • 1,对应常量OPENED,表示send()方法还没有被调用,仍然可以使用setRequestHeader(),设定HTTP请求的头信息。
  • 2,对应常量HEADERS_RECEIVED,表示send()方法已经执行,并且头信息和状态码已经收到。
  • 3,对应常量LOADING,表示正在接收服务器传来的body部分的数据,如果responseType属性是text或者空字符串,responseText就会包含已经收到的部分信息。
  • 4,对应常量DONE,表示服务器数据已经完全接收,或者本次接收已经失败了。

在通信过程中,每当发生状态变化的时候,readyState属性的值就会发生改变。这个值每一次变化,都会触发readyStateChange事件。

2.2 onreadystatechange

onreadystatechange属性指向一个回调函数,当readystatechange事件发生的时候,这个回调函数就会调用,并且XMLHttpRequest实例的readyState属性也会发生变化。

另外,如果使用abort()方法,终止XMLHttpRequest请求,onreadystatechange回调函数也会被调用。

var xmlhttp = new XMLHttpRequest();
xmlhttp.open( 'GET', 'http://example.com' , true );
xmlhttp.onreadystatechange = function () {
  if ( XMLHttpRequest.DONE != xmlhttp.readyState ) {
    return;
  }
  if ( 200 != xmlhttp.status ) {
    return;
  }
  console.log( xmlhttp.responseText );
};
xmlhttp.send();

2.3 response

response属性为只读,返回接收到的数据体(即body部分)。它的类型可以是ArrayBuffer、Blob、Document、JSON对象、或者一个字符串,这由XMLHttpRequest.responseType属性的值决定。

如果本次请求没有成功或者数据不完整,该属性就会等于null

2.4 responseType

responseType属性用来指定服务器返回数据(xhr.response)的类型

  • "":字符串(默认值)
  • "arraybuffer":ArrayBuffer对象
  • "blob":Blob对象
  • "document":Document对象
  • "json":JSON对象
  • "text":字符串

text类型适合大多数情况,而且直接处理文本也比较方便,document类型适合返回XML文档的情况,blob类型适合读取二进制数据,比如图片文件。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status == 200) {
    var blob = new Blob([this.response], {type: 'image/png'});
    // 或者
    var blob = oReq.response;
  }
};

xhr.send();

如果将这个属性设为ArrayBuffer,就可以按照数组的方式处理二进制数据。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  var uInt8Array = new Uint8Array(this.response);
  for (var i = 0, len = binStr.length; i < len; ++i) {
  // var byte = uInt8Array[i];
  }
};

xhr.send();

如果将这个属性设为“json”,支持JSON的浏览器(Firefox>9,chrome>30),就会自动对返回数据调用JSON.parse() 方法。也就是说,你从xhr.response属性(注意,不是xhr.responseText属性)得到的不是文本,而是一个JSON对象

XHR2支持Ajax的返回类型为文档,即xhr.responseType="document" 。这意味着,对于那些打开CORS的网站,我们可以直接用Ajax抓取网页,然后不用解析HTML字符串,直接对XHR回应进行DOM操作。

2.5 responseText

responseText属性为只读,返回接收到的字符串。如果本次请求没有成功或者数据不完整,该属性就会等于null

2.6 responseXML

responseXML属性为只读,返回接收到的Document对象。如果本次请求没有成功或者数据不完整ontaining,或者不能被解析为XML或HTML,该属性等于null。

返回的数据会被解析为text/xml类型的数据流。如果responseType设为document,且这个请求是异步的,则返回的数据会被解析为text/html数据流。

如果服务器返回的数据,没有明示Content-Type头信息等于text/xml,可以使用overrideMimeType()方法,指定XMLHttpRequest对象将返回的数据解析为XML。

2.7 status

status属性为只读属性,表示本次请求所得到的HTTP状态码,它是一个整数。一般来说,如果通信成功的话,这个状态码是200

2.8 statusText

statusText属性为只读属性,返回一个字符串,表示服务器发送的状态提示。不同于status属性,该属性包含整个状态信息,比如”200 OK“。

2.9 timeout

timeout属性等于一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制

  var xhr = new XMLHttpRequest();
  xhr.ontimeout = function () {
    console.error("The request for " + url + " timed out.");
  };
  xhr.onload = function() {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        callback.apply(xhr, args);
      } else {
        console.error(xhr.statusText);
      }
    }
  };
  xhr.open("GET", url, true);
  xhr.timeout = timeout;
  xhr.send(null);
}

2.10 ontimeout

ontimeout属性指向一个回调函数,发生timeout事件时,该函数会被调用

2.11 withCredentials(跨域)

withCredentials属性是一个布尔值,表示跨域请求时,用户信息(比如cookie和认证的HTTP头信息)是否会包含在请求之中,默认为false。

3 XMLHttpRequest实例的方法

3.1 abort()

abort方法用来终止已经发出的HTTP请求。

3.2 getAllResponseHeaders()

getAllResponseHeaders方法返回服务器发来的所有HTTP头信息。格式为字符串,每个头信息之间使用CRLF(换行回车)分隔,如果没有受到服务器回应,该属性返回null

3.3 getResponseHeader()

getResponseHeader方法返回HTTP头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,则该属性为null

function getHeaderTime () {
  console.log(this.getResponseHeader("Last-Modified"));
}

var oReq = new XMLHttpRequest();
oReq.open("HEAD", "yourpage.html");
oReq.onload = getHeaderTime;
oReq.send();

如果有多个字段同名,则它们的值会被连接为一个字符串,每个字段之间使用”逗号+空格“分隔

3.4 open() 打开路径

XMLHttpRequest对象的open方法用于指定发送HTTP请求的参数,它的使用格式如下,一共可以接受五个参数。

void open(
   string method,
   string url,
   optional boolean async,
   optional string user,
   optional string password
);
  • method:表示HTTP动词,比如“GET”、“POST”、“PUT”和“DELETE”。
  • url: 表示请求发送的网址。
  • async: 格式为布尔值,默认为true,表示请求是否为异步。如果设为false,则send()方法只有等到收到服务器返回的结果,才会有返回值。
  • user:表示用于认证的用户名,默认为空字符串。
  • password:表示用于认证的密码,默认为空字符串。

如果对使用过open()方法的请求,再次使用这个方法,等同于调用abort()

下面发送POST请求的例子。

xhr.open('POST', encodeURI('someURL'));
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {};
xhr.send(encodeURI('dataString'));

上面方法中,open方法向指定URL发出POST请求,send方法送出实际的数据。

下面是一个同步AJAX请求的例子。

var request = new XMLHttpRequest();
request.open('GET', '/bar/foo.txt', false);
request.send(null);

if (request.status === 200) {
  console.log(request.responseText);
}

3.5 send() 发送参数

send方法用于实际发出HTTP请求如果不带参数,就表示HTTP请求只包含头信息,也就是只有一个URL,典型例子就是GET请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是POST请求。

如果请求是异步的(默认为异步),该方法在发出请求后会立即返回。如果请求为同步,该方法只有等到收到服务器回应后,才会返回。

注意,所有XMLHttpRequest的监听事件,都必须在send()方法调用之前设定。

该方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。

void send();
void send(ArrayBufferView data);
void send(Blob data);
void send(Document data);
void send(String data);
void send(FormData data);

如果发送Document数据,在发送之前,数据会先被串行化。

发送二进制数据,最好使用ArrayBufferViewBlob对象,这使得通过Ajax上传文件成为可能。

下面是一个上传ArrayBuffer对象的例子。

function sendArrayBuffer() {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  var uInt8Array = new Uint8Array([1, 2, 3]);

  xhr.send(uInt8Array.buffer);
}

FormData类型可以用于构造表单数据。

连接Form表单中FormData类型

3.6 setRequestHeader()

setRequestHeader方法用于设置HTTP头信息。该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

上面代码首先设置头信息Content-Type,表示发送JSON格式的数据;然后设置Content-Length,表示数据长度;最后发送JSON数据。

3.7 overrideMimeType()

该方法用来指定服务器返回数据的MIME类型。该方法必须在send()之前调用

传统上,如果希望从服务器取回二进制数据,就要使用这个方法,人为将数据类型伪装成文本数据。

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);

// 强制将MIME改为文本类型
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
  if (this.readyState == 4 && this.status == 200) {
    var binStr = this.responseText;
    for (var i = 0, len = binStr.length; i < len; ++i) {
      var c = binStr.charCodeAt(i);
      var byte = c & 0xff;  // 去除高位字节,留下低位字节
    }
  }
};

xhr.send();

上面代码中,因为传回来的是二进制数据,首先用xhr.overrideMimeType方法强制改变它的MIME类型,伪装成文本数据。字符集必需指定为“x-user-defined”,如果是其他字符集,浏览器内部会强制转码,将其保存成UTF-16的形式。字符集“x-user-defined”其实也会发生转码,浏览器会在每个字节前面再加上一个字节(0xF700-0xF7ff),因此后面要对每个字符进行一次与运算(&),将高位的8个位去除,只留下低位的8个位,由此逐一读出原文件二进制数据的每个字节。

注意:这种方法很麻烦,在XMLHttpRequest版本升级以后,一般采用指定responseType的方法。

var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
  var arraybuffer = xhr.response;
  // ...
}
xhr.open("GET", url);
xhr.responseType = "arraybuffer";
xhr.send();

4 XMLHttpRequest实例的事件

4.1 readyStateChange事件

readyState属性的值发生改变,就会触发readyStateChange事件

我们可以通过onReadyStateChange属性,指定这个事件的回调函数,对不同状态进行不同处理。尤其是当状态变为4的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。

4.2 progress事件

上传文件时,XMLHTTPRequest对象的upload属性有一个progress,会不断返回上传的进度

假定网页上有一个progress元素。

<progress min="0" max="100" value="0">0% complete</progress>

文件上传时,对upload属性指定progress事件回调函数,即可获得上传的进度。

function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  // Listen to the upload progress.
  var progressBar = document.querySelector('progress');
  xhr.upload.onprogress = function(e) {
    if (e.lengthComputable) {
      progressBar.value = (e.loaded / e.total) * 100;
      progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
    }
  };

  xhr.send(blobOrFile);
}

upload(new Blob(['hello world'], {type: 'text/plain'}));

4.3 load事件、error事件、abort事件

load事件表示服务器传来的数据接收完毕error事件表示请求出错abort事件表示请求被中断

var xhr = new XMLHttpRequest();

xhr.addEventListener("progress", updateProgress);
xhr.addEventListener("load", transferComplete);
xhr.addEventListener("error", transferFailed);
xhr.addEventListener("abort", transferCanceled);

xhr.open();

function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var percentComplete = oEvent.loaded / oEvent.total;
    // ...
  } else {
    // 回应的总数据量未知,导致无法计算百分比
  }
}

function transferComplete(evt) {
  console.log("The transfer is complete.");
}

function transferFailed(evt) {
  console.log("An error occurred while transferring the file.");
}

function transferCanceled(evt) {
  console.log("The transfer has been canceled by the user.");
}

4.4 loadend事件

abortloaderror这三个事件,会伴随一个loadend事件,表示请求结束,但不知道其是否成功。

req.addEventListener("loadend", loadEnd);

function loadEnd(e) {
  alert("请求结束(不知道是否成功)");
}

5 JSONP

JSONP是一种常见做法,用于服务器与客户端之间的数据传输,主要为了规避浏览器的同域限制。因为Ajax只能向当前网页所在的域名发出HTTP请求(除非使用下文要提到的CORS,但并不是所有服务器都支持CORS),所以JSONP就采用在网页中动态插入script元素的做法,向服务器请求脚本文件。

function addScriptTag(src){
    var script = document.createElement('script');
    script.setAttribute("type","text/javascript");
    script.src = src;
    document.body.appendChild(script);
}

window.onload = function(){
    addScriptTag("http://example.com/ip?callback=foo");
}

function foo(data) {
    console.log('Your public IP address is: ' + data.ip);
};

上面代码使用了JSONP,运行以后当前网页就可以直接处理example.com返回的数据了。

由于script元素返回的脚本文件,是直接作为代码运行的,不像Ajax请求返回的是JSON字符串,需要用JSON.parse方法将字符串转为JSON对象。于是,为了方便起见,许多服务器支持JSONP指定回调函数的名称,直接将JSON数据放入回调函数的参数,如此一来就省略了将字符串解析为JSON对象的步骤。

请看下面的例子,假定访问 http://example.com/ip,返回如下JSON数据:

{"ip":"8.8.8.8"}

现在服务器允许客户端请求时使用callback参数指定回调函数。访问http://example.com/ip?callback=foo,返回的数据变成:

foo({"ip":"8.8.8.8"})

这时,如果客户端定义了foo函数,该函数就会被立即调用,而作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

function foo(data) {
    console.log('Your public IP address is: ' + data.ip);
};

jQuery的getJSON方法就是JSONP的一个应用。

$.getJSON( "http://example.com/api", function (data){ .... })

$.getJSON方法的第一个参数是服务器网址,第二个参数是回调函数,该回调函数的参数就是服务器返回的JSON数据。

JSONP只支持GET请求,JSONP的优势在于可以用于老式浏览器,以及可以向不支持CORS的网站请求数据。

想查看ajax中用CORS这里的链接

想查看XMLhttpRequest升级的Fetch API这里的链接

猜你喜欢

转载自blog.csdn.net/u012060033/article/details/89958461
今日推荐