异步请求

一、异步

JavaScript中主要有四种异步的实现方式。

1. 回调函数

使用回调函数的缺点是,多个回调函数嵌套的时候会造成回调函数地狱,代码耦合度高,不利于代码维护。

2. Promise

1)概念:

  • Promise对象是一个构造函数,用来生成Promise实例。
  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
  • Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  • Promise对象的状态改变,只有两种可能:从pending变为 fulfilled 和从pending变为 rejected 。要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。

2)用法:

// 基本
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
// 链式
promise.then(function(value) {
  // success
}, function(error) {
  // failure
});
promise.then(result => console.log(result));
promise.catch(error => console.log(error));

3. Generator 函数

1)概念:

  • Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态。

  • 执行 Generator 函数会返回一个遍历器对象,可以是一个遍历器对象生成函数。

  • Generator.prototype.next()

    返回一个由 yield表达式生成的值。

    Generator.prototype.return()

    返回给定的值并结束生成器。

    Generator.prototype.throw()

    向生成器抛出一个错误。

2)用法:

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
hw.next()  // { value: 'hello', done: false }
hw.next()  // { value: 'world', done: false }
hw.next()  // { value: 'ending', done: true }
hw.next()  // { value: undefined, done: true }
for (let v of helloWorldGenerator()) {
  console.log(v);
}
// hello,world,ending

4. async 函数

1)概念:

  • 本质上是是 Generator 函数的语法糖。
  • 将 Generator 函数的星号(*)替换成async,将yield替换成await。
  • 改进:拥有更好的语义,返回Promise对象,函数体内用 await 表示后面有需要等待的异步操作,await 还能接原始类型。

2)用法:

// 例一
function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}
async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000);
  }
}
one2FiveInAsync();
// 例二
async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

二、请求

AJAX 即”Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。

1. XMLHttpRequest

xhr.readyState

  • 0 - (未初始化),还没有调用send()
  • 1 - (载入),已调用send(),正在发送请求
  • 2 - (载入完成),send()方法执行完成,已接收到全部的响应内容
  • 3 - (交互),正在解析响应内容
  • 4 - (完成),响应内容解析完成,可以在客户端调用

xhr.status

  • 1xx - 指示信息,表示资源已经请求,继续处理
  • 2xx - 表示成功处理请求,如200
  • 3xx - 需要重定向,浏览器直接跳转
    • 301 - 永久重定向,每次访问A地址的时候,浏览器会自动跳到B地址
    • 302 - 临时重新向,仅一次访问A地址时调到B,下次访问还是跳到A
    • 304 - 资源未改变,若请求返回的资源与之前的相比没发生改变,服务器会返回304,浏览器会使用自己先前缓存的资源
  • 4xx - 客户端请求错误
    • 400 - 客户端请求有语法错误,不能被服务器端所理解
    • 401 - 请求未经过授权,这个状态码必须和WWW-Authenticate报头域一起使用
    • 403 - 服务器收到请求,但是拒绝提供该服务
    • 404 - 请求的地址错误
  • 5xx - 服务端错误
    • 500 - 服务器发生不可预期的错误
    • 503 - 服务器当前不能处理客户端的请求,一段时间后可能恢复正常

示例:

// 基本过程
new -> open() -> send(),设置监听函数,等待响应。
// get请求
const xhr = new XMLHttpRequest()
xhr.open('GET', './data/test.json', true)
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      // ...
    } else {
      // ...
    }
  }
}
xhr.send(null)
// post请求
const xhr = new XMLHttpRequest()
xhr.open('POST', '/login', true)
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      // ...
    } else {
      // ...
    }
  }
}
const postData = {
  name: 'zhangsan',
  password: '123'
}
xhr.send(JSON.stringify(postData))

2. Fetch API

一个全局 API ,可用于发送请求,返回 Promise 对象。

注意:当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。

fetch('http://example.com/movies.json',[{options}])
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

3. Axios

一个基于 promise 的 HTTP 库,可以用在浏览器和 Node.js 中。

$ npm install axios

主要方法:

axios.create([config])

axios.get(url[, config])

axios.post(url[, data[, config]])

import axios from 'axios'
let instance = axios.create({
  baseURL: 'https://some-domain.com/api/'
    ...
});
// 可以直接使用引入的对象发起请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
// 可以使用实例发起请求
instance.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
// 其他请求方式同理
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

实际开发中会把函数设置为async,并在请求前加上await。

响应报文:

{
  data: {},  // `data` 由服务器提供的响应
  status: 200,  // `status` 来自服务器响应的 HTTP 状态码
  statusText: 'OK',  // `statusText` 来自服务器响应的 HTTP 状态信息
  headers: {},  // `headers` 服务器响应的头
  config: {}  // `config` 是为请求提供的配置信息
}
axios.get('/user/12345')
  .then(function(response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });

三、URI/URL

URI: Uniform Resource Identifier 指的是统一资源标识符
URL: Uniform Resource Location 指的是统一资源定位符
URN: Universal Resource Name 指的是统一资源名称

URI 指的是统一资源标识符,用唯一的标识来确定一个资源,它是一种抽象的定义,不管使用什么方法来定义,只要能唯一的标识一个资源,就可以称为 URI。

URL 指的是统一资源定位符,URN 指的是统一资源名称。URL 和 URN 是 URI 的子集,URL 可以理解为使用地址来标识资源,URN 可以理解为使用名称来标识资源。

// 假设当前url为: https://coding.imooc.com/lesson/115.html&a=100#mid=30378
console.log(location.protocol)  // 'https:'
console.log(location.host)      // 'coding.imooc.com'
console.log(location.pathname)  // '/lesson/115.html'
console.log(location.search)    // '?a=100'
console.log(location.hash)      // '#mid=30378'

四、参考

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API

https://es6.ruanyifeng.com/

https://www.kancloud.cn/yunye/axios/234845


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

应用层协议 Previous
对DOM和Virtual DOM的理解 Next