当前位置:   article > 正文

异步请求的方法

异步请求

1. AJAX

AJAX ( Asynchronous JavaScript and XML ) 异步的 JS 和 XML,通过 AJAX 可以在浏览器中向服务器发送异步请求,核心是XMLHttpRequest

  1. 无需刷新页面获取数据
  2. 根据用户事件来更新页面部分内容
1.1. XMLHttpRequest 中基本的方法有
  • abort() : 停止发送当前请求
  • getAllResponseHeaders() : 获取服务器返回的全部响应头
  • getRequestHeader(“headerLabel”):根据指定的请求头, 获取相应的值
  • open( “method” , “url” [ , asyncFlag , “uname” , “passwd” ] ):建立与服务器 URL 的链接并设置请求的方法以及是否使用异步请求(默认为支持异步请求)
  • asyncFlag 处的为 true 表示支持异步请求, false 表示不支持。如果远程链接需要用户名和密码, 则可以在参数中提供。
  • send( content ) : 发送请求 ( 其中 content 是请求参数)
  • setRequestHeader( “label” , “value” ):发送请求之前, 先设置请求报头
1.2. XMLHttpRequest 中的常用属性
  • onreadystatechange:用于指定 XMLHttpRequest 对象状态改变时对应的时间处理函数
  • readyState:XMLHttpRequest 对象的处理状态
  • responseText:该属性用于获取服务器的响应文本
  • responseXML:该属性用于获取服务器响应的XML文档对象
  • status:对应服务器返回的状态代码(只有当服务器的响应完成时, 才会有该状态代码)
  • statusText : 对应服务器返回的状态文本信息(当服务器响应完成时)
  • onreadystatechange 属性 :用于指定一个函数 ,这个函数负责处理 XMLHttpRequest 状态变化时的事件
  • readyState 属性 : 对应 XMLHttpRequest 对象的状态。XMLHttpRequest 的状态有
    • 0 : 表示 XMLHttpRequest 对象还没有完成初始化
    • 1 : 表示 XMLHttpRequest 对象开始发送请求
    • 2 : 表示 XMLHttpRequest 对象请求发送完成
    • 3 : 表示 XMLHttpRequest 对象开始读取服务器响应信息
    • 4 : 表示 XMLHttpRequest 对象读取服务器响应结束
  • status 属性 : 对应服务器响应代码, 用于判断服务器响应是否正确
  • statusText 属性:用于获取服务器响应码对应的响应信息。 无论响应代码是什么, 只要生成了响应, 就可以获取响应信息。
  • responseText 属性:获取服务器发送来的文本信息(如果服务器发送的是文本信息)。异步数据传输中, JSON 格式的数据通常使用该属性获取
  • responseXML 属性 : 如果服务器响应发送的是 XML 格式的数据, 用该属性获取。(早期的 AJAX 应用中, 传输的数据都是 XML 格式的)
1.3. 使用XMLHttpRequest进行异步通信的步骤
  • 初始化 XMLHttpRequest 对象 : 视不同浏览器创建 XMLHttpRequest 对象

  • 打开与服务器的连接 : 使用 open() 方法开启连接

  • 设置监听 XMLHttpRequest 状态改变的事件处理函数。可以在 onreadystatechange 之后直接书写函数,也可以单独书写函数, 然后在这里指定函数名即可。

  • 设置请求报头。一般要设置跟当前请求有关的报头, 比如编码等。使用 setRequestHeader() 方法设定

  • 发送请求。向服务器发请求,如果是 POST 请求, 可以发送参数, 否则发送 null 即可。

  • // AJAX基础步骤
    
    // 1. 创建 xhrHttpRequest 对象
    let xhr;
    if (window.xhrRequest) {
    	// code for IE7+, Firefox, Chrome, Opera, Safari
    	xhr = new xhrHttpRequest();
    } else {
    	// code for IE6, IE5
    	xhr = new ActiveXObject("Microsoft.xhr");
    }
    // 设置JSON格式自动转换
    xhr.responseType='json';
    
    // 2. 设置请求方法和url
    // GET请求
    xhr.open("GET", "http://127.0.0.1:3300/server?name=zs&password=123", true);
    
    // POST请求
    // xhr.open('POST','http://127.0.0.1:3300/server')
    // xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
    // xhr.send(param) post请求参数放在 send 方法中
    
    // 3. 发送
    xhr.send();
    // 4.事件绑定 处理服务端返回的结果
    xhr.onreadystatechange = function () {
    	if (xhr.readyState == 4) {
    		if (xhr.status >= 200 && xhr.status < 300) {
                // 手动转换 JSON 数据转为JS对象 JSON.parse(xhr.response)
    			console.log(xhr.status); // 状态码
    			console.log(xhr.statusText); // 状态字符串
    			console.log(xhr.getAllResponseHeaders()); // 所有响应头
    			console.log(xhr.response); // 响应体
    		}
    	}
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
  • // 引入express模块
    const express = require('express')
    // 创建应用对象
    const app = express()
    const port = 3000
    
    // 创建路由规则
    // req 对请求报文的封装
    // res 对响应报文的封装
    app.get('/', (req, res) => {
        // JSON格式 JSON.stringify(data)
      res.send('Hello World!')
    })
    
    // 如果参数值中有中文,比如张三丰,则用encodeURI对中文进行编码 data = "username="+encodeURI("张三丰")+"&password=2016";
    
    // app.post("/", function (req, res) {
    // 	res.send("POST request to the homepage");
    // });
    
    // app.all("/secret", function (req, res, next) {
    // 设置响应头,允许跨域
    	response.setHeader('Access-Control-Allow-Origin','*');
    // 允许任何类型的请求头
    	response.setHeader('Access-Control-Allow-Headers','*')
    // 	console.log("Accessing the secret section ...");
    // 	next(); // pass control to the next handler
    // });
    
    // 监听端口启动服务
    app.listen(port, () => {
      console.log(`Example app listening at http://localhost:${port}`)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
1.4. nodemon

修改服务器代码后,自动重启的一个工具,nodemon server.js

1.5. AJAX-IE 缓存问题
// 解决 IE 缓存
xhr.open("GET",'http://127.0.0.1:3300/ie?t='+Date.now())
  • 1
  • 2
1.6. 请求超时与网络异常
// 超时设置 2s
xhr.timeout=2000;
// 超时回调
xhe.ontimeout = function(){}
// 服务器设置定时器

// 网络异常回调
xhr.onerror = function(){}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
1.7. 取消请求
// 停止发送当前请求
xhr.abort()
  • 1
  • 2
1.8. 重复发送请求
let x =null;
let isSending = false; // 是否正在发送AJAX请求

if(isSending){
	x.abort(); //如果正在发送,则取消该请求,创建一个新的请求
}
x= new XMLHTTPRequest();
isSending = true;

xhr.open("GET", "http://127.0.0.1:3300/delay", true);

x.send();
// 4.事件绑定 处理服务端返回的结果
x.onreadystatechange = function () {
	if (x.readyState == 4) {
        isSending = false;
	}
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
1.9. 模仿JQuery ajax()方法封装函数
function ajax(obj){
    // 默认参数
    let defaults = {
        type : 'get',
        data : {},
        url : '#',
        dataType : 'text',
        async : true,
        success : function(data){console.log(data)}
    }
    // 处理形参,传递参数的时候就覆盖默认参数,不传递就使用默认参数
    for(let key in obj){
        defaults[key] = obj[key];
    }
    // 1、创建XMLHttpRequest对象
    let xhr = null;
    if(window.XMLHttpRequest){
        xhr = new XMLHttpRequest();
    }else{
        xhr = new ActiveXObject('Microsoft.XMLHTTP');
    }
    // 把对象形式的参数转化为字符串形式的参数
    /*
    {username:'zhangsan','password':123}
    转换为
    username=zhangsan&password=123
    */
    let param = '';
    for(let attr in defaults.data){
        param += attr + '=' + defaults.data[attr] + '&';
    }
    if(param){
        param = param.substring(0,param.length - 1);
    }
    // 处理get请求参数并且处理中文乱码问题
    if(defaults.type == 'get'){
        defaults.url += '?' + encodeURI(param);
    }
    // 2、准备发送(设置发送的参数)
    xhr.open(defaults.type,defaults.url,defaults.async);
    // 处理post请求参数并且设置请求头信息(必须设置)
    let data = null;
    if(defaults.type == 'post'){
        data = param;
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
    }
    // 3、执行发送动作
    xhr.send(data);
    // 处理同步请求,不会调用回调函数
    if(!defaults.async){
        if(defaults.dataType == 'json'){
            return JSON.parse(xhr.responseText);
        }else{
            return xhr.responseText;
        }
    }
    // 4、指定回调函数(处理服务器响应数据)
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4){
            if(xhr.status == 200){
                let data = xhr.responseText;
                if(defaults.dataType == 'json'){
                    // data = eval("("+ data +")");
                    data = JSON.parse(data);
                }
                defaults.success(data);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

2. JQuery Ajax()

2.1. get 请求

$.get(url, [data], [callback], [type])

  1. url:请求的 URL 地址。
  2. data:请求携带的参数。
  3. callback:载入成功时回调函数。
  4. type:设置返回内容格式,xml, html, script, json, text, _default。
2.2. post 请求

$.post(url, [data], [callback], [type])

  1. url:请求的 URL 地址。
  2. data:请求携带的参数。
  3. callback:载入成功时回调函数。
  4. type:设置返回内容格式,xml, html, script, json, text, _default。
2.3. 通用方法
$.ajax({
    // url
	url: 'http://127.0.0.1:3300/jquery-server',
    // 参数
    data: {a:1, b:2},
    // 请求类型
    type: 'GET',
    // 响应体结果类型
    dataType: 'json'
    // 成功的回调
    success: function(data){
        console.log(data);
    },
    // 超时时间
    timeout: 2000,
    error: function(error){
        console.log(error);
    },
    // 头信息
    headers: {
        c:300,
        d:400
    }
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

3. Axios

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

3.1. GET请求
// 为给定 ID 的 user 创建请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// 上面的请求也可以这样做
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
3.2. POST请求
axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3.3. 并发请求
// 处理并发请求的助手函数
// axios.all(iterable)
// axios.spread(callback)

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()])
  .then(axios.spread(function (acct, perms) {
    // 两个请求现在都执行完成
  }));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
3.4. 通用方法
// 发送 GET 请求(默认的方法)
axios('/user/12345');

// 发送 POST 请求
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

// 获取远端图片
axios({
  method:'get',
  url:'http://bit.ly/2mTM3nY',
  responseType:'stream'
})
  .then(function(response) {
  response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
3.5. 请求方法别名
  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])
3.5.1. 注意

在使用别名方法时, urlmethoddata 这些属性都不必在配置中指定。

3.6. 请求配置

这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。

{
   // `url` 是用于请求的服务器 URL
  url: '/user',

  // `method` 是创建请求时使用的方法
  method: 'get', // default

  // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
  // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
  baseURL: 'https://some-domain.com/api/',

  // `transformRequest` 允许在向服务器发送前,修改请求数据
  // 只能用在 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 后面数组中的函数必须返回一个字符串,或 ArrayBuffer,或 Stream
  transformRequest: [function (data, headers) {
    // 对 data 进行任意转换处理
    return data;
  }],

  // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 对 data 进行任意转换处理
    return data;
  }],

  // `headers` 是即将被发送的自定义请求头
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // `params` 是即将与请求一起发送的 URL 参数
  // 必须是一个无格式对象(plain object)或 URLSearchParams 对象
  params: {
    ID: 12345
  },

   // `paramsSerializer` 是一个负责 `params` 序列化的函数
  // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
  paramsSerializer: function(params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // `data` 是作为请求主体被发送的数据
  // 只适用于这些请求方法 'PUT', 'POST', 和 'PATCH'
  // 在没有设置 `transformRequest` 时,必须是以下类型之一:
  // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器专属:FormData, File, Blob
  // - Node 专属: Stream
  data: {
    firstName: 'Fred'
  },

  // `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
  // 如果请求话费了超过 `timeout` 的时间,请求将被中断
  timeout: 1000,

   // `withCredentials` 表示跨域请求时是否需要使用凭证
  withCredentials: false, // default

  // `adapter` 允许自定义处理请求,以使测试更轻松
  // 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
  adapter: function (config) {
    /* ... */
  },

 // `auth` 表示应该使用 HTTP 基础验证,并提供凭据
  // 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

   // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType: 'json', // default

  // `responseEncoding` indicates encoding to use for decoding responses
  // Note: Ignored for `responseType` of 'stream' or client-side requests
  responseEncoding: 'utf8', // default

   // `xsrfCookieName` 是用作 xsrf token 的值的cookie的名称
  xsrfCookieName: 'XSRF-TOKEN', // default

  // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

   // `onUploadProgress` 允许为上传处理进度事件
  onUploadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
  },

  // `onDownloadProgress` 允许为下载处理进度事件
  onDownloadProgress: function (progressEvent) {
    // 对原生进度事件的处理
  },

   // `maxContentLength` 定义允许的响应内容的最大尺寸
  maxContentLength: 2000,

  // `validateStatus` 定义对于给定的HTTP 响应状态码是 resolve 或 reject  promise 。如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),promise 将被 resolve; 否则,promise 将被 rejecte
  validateStatus: function (status) {
    return status >= 200 && status < 300; // default
  },

  // `maxRedirects` 定义在 node.js 中 follow 的最大重定向数目
  // 如果设置为0,将不会 follow 任何重定向
  maxRedirects: 5, // default

  // `socketPath` defines a UNIX Socket to be used in node.js.
  // e.g. '/var/run/docker.sock' to send requests to the docker daemon.
  // Only either `socketPath` or `proxy` can be specified.
  // If both are specified, `socketPath` is used.
  socketPath: null, // default

  // `httpAgent` 和 `httpsAgent` 分别在 node.js 中用于定义在执行 http 和 https 时使用的自定义代理。允许像这样配置选项:
  // `keepAlive` 默认没有启用
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // 'proxy' 定义代理服务器的主机名称和端口
  // `auth` 表示 HTTP 基础验证应当用于连接代理,并提供凭据
  // 这将会设置一个 `Proxy-Authorization` 头,覆写掉已有的通过使用 `header` 设置的自定义 `Proxy-Authorization` 头。
  proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

  // `cancelToken` 指定用于取消请求的 cancel token
  // (查看后面的 Cancellation 这节了解更多)
  cancelToken: new CancelToken(function (cancel) {
  })
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
3.7. 响应结构

某个请求的响应包含以下信息

{
  // `data` 由服务器提供的响应
  data: {},

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 服务器响应的头
  headers: {},

   // `config` 是为请求提供的配置信息
  config: {},
 // 'request'
  // `request` is the request that generated this response
  // It is the last ClientRequest instance in node.js (in redirects)
  // and an XMLHttpRequest instance the browser
  request: {}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

使用 then 时,你将接收下面这样的响应 :

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);
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在使用 catch 时,或传递 rejection callback 作为 then 的第二个参数时,响应可以通过 error 对象可被使用,正如在错误处理这一节所讲。

3.8. 配置默认值

你可以指定将被用在各个请求的配置默认值

3.8.1. 全局的 axios 默认值
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  • 1
  • 2
  • 3
3.8.2. 自定义实例默认值
// Set config defaults when creating the instance
const instance = axios.create({
  baseURL: 'https://api.example.com'
});

// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.8.3. 配置的优先顺序

配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:

// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();

// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;

// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
  timeout: 5000
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
3.9. axios实例对象
3.9.1. 创建实例

可以使用自定义配置新建一个 axios 实例

axios.create([config])

const instance = axios.create({
  baseURL: 'https://some-domain.com/api/',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
3.10. 拦截器

在请求或响应被 thencatch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    console.log("请求拦截器 成功");
    return config;
  }, function (error) {
    // 对请求错误做些什么
    console.log("请求拦截器 失败");
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    console.log("响应拦截器 成功");
    return response;
  }, function (error) {
    // 对响应错误做点什么
    console.log("响应拦截器 失败");
    return Promise.reject(error);
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

如果你想在稍后移除拦截器,可以这样:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
  • 1
  • 2

可以为自定义 axios 实例添加拦截器

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
  • 1
  • 2
3.11. 取消请求(cancel token )

使用 cancel token 取消请求

Axios 的 cancel token API 基于cancelable promises proposal,它还处于第一阶段。

可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
     // 处理错误
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
  // cancelToken: new axios.CancelToken(function (c) {
  //  cancel = c;
  })
});

// cancel the request
cancel();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

注意: 可以使用同一个 cancel token 取消多个请求

3.12. 源码阅读

4. fetch函数

fetch("http://example.com/movies.json", {
	// 请求方法
	method: "POST",
	headers: {
		name: "zs",
	},
	body: "username=admin&password=admin",
})
	.then(function (response) {
		return response.json();
	})
	.then(function (myJson) {
		console.log(myJson);
	});

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5. 跨域

5.1. 同源策略

同源策略(Same-Origin Policy)最早由 Netscape 公司提出,是浏览器的一种安全策略。

同源: 协议、域名、端口号 必须完全相同, 违背同源策略就是跨域。

5.2. JSONP

JSONP ( JSON with Padding ),是一个非官方的跨域解决方案,只支持 get 请求。

JSONP 利用 script 标签的跨域能力来发送请求的。

JSONP :

// 1. 动态的创建一个 script 标签 
let script = document.createElement("script"); 

// 2. 设置 script 的 src,设置回调函数 
script.src = "http://localhost:3000/testAJAX?callback=abc"; 
function abc(data) {
    alert(data.name); 
}; 

// 3. 将 script 添加到 body 中 
document.body.appendChild(script); 

// 4. 服务器中路由的处理 
router.get("/testAJAX" , function (req , res) { 
    console.log("收到请求"); 
    let callback = req.query.callback; 
    let obj = { name:"孙悟空", age:18 } 
    res.send(callback+"("+JSON.stringify(obj)+")"); 
    // res.end(`callback(${JSON.stringify(obj)})`)
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
5.3. JQuery -jsonp
  <script src="http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
  
<!-- 动态创建script标签 -->
  <script>
    function hello(data) {
      console.log(data)
    }
    let script = document.createElement('script');
    script.src = 'https://www.baidu.com/jquery-server?callback=?'
    let body = document.getElementsByTagName('body')[0]
    body.appendChild(script)
  </script>
  
  <script type="text/javascript">
    $.getJSON("http://127.0.0.1:3300/jquery-server?callback=?", function(data) {
		console.log(data)
    });
  </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
5.4. 模仿jQuery封装jsonp
function ajax(obj){
    // jsonp仅仅支持get请求
    var defaults = {
        url : '#',
        dataType : 'jsonp',
        jsonp : 'callback',
        data : {},
        success:function(data){console.log(data);}
    }
    for(var key in obj){
        defaults[key] = obj[key];
    }
    // 这里是默认的回调函数名称
    // expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
    var cbName = 'jQuery' + ('1.11.1' + Math.random()).replace(/\D/g,"") + '_' + (new Date().getTime());
    if(defaults.jsonpCallback){
        cbName = defaults.jsonpCallback;
    }
    // 这里就是回调函数,调用方式:服务器响应内容来调用
    // 向window对象中添加了一个方法,方法名称是变量cbName的值
    window[cbName] = function(data){
        defaults.success(data);//这里success的data是实参
    }
    var param = '';
    for(var attr in defaults.data){
        param += attr + '=' + defaults.data[attr] + '&';
    }
    if(param){
        param = param.substring(0,param.length-1);
        param = '&' + param;
    }
    var script = document.createElement('script');
    script.src = defaults.url + '?' + defaults.jsonp + '=' + cbName + param;
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(script);
    // abc({"username":"zhangsan","password":"123"})
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

6. CORS

MDN

​ CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方 案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

​ CORS 是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应 以后就会对响应放行。

// 引入express模块
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
    // JSON格式 JSON.stringify(data)
  res.send('Hello World!')
})
// 如果参数值中有中文,比如张三丰,则用encodeURI对中文进行编码 data = "username="+encodeURI("张三丰")+"&password=2016";


// 设置响应头,允许跨域
	response.setHeader('Access-Control-Allow-Origin','*');
// 允许任何类型的请求头
	response.setHeader('Access-Control-Allow-Headers','*')
// 设置请求方法
	response.setHeader('Access-Control-Allow-Method','*')

// 监听端口启动服务
app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

7. Promise

​ 语法上Promise是一个构造函数,内部封装异步操作并可以获取其成功或失败的结果,异步编程的一种解决方案,用来解决回调地狱(在回调函数外面处理接收的数据)。

Promise三种状态:

  1. pending[等待]等待状态
  2. fulfilled[满足]满足状态
  3. rejected[拒绝]拒绝状态

当promise状态发生改变,就会触发then()里的响应函数处理后续步骤。

Promise状态改变只有:

  1. 从pending变为fulfilled
  2. 从pending变为rejected

promise状态一经改变,不会再变。

const p = new Promise((resolve, reject) => {
	setTimeout(function () {
		resolve("数据!"); // pending --> fulfilled
		reject("error!"); // pending --> rejected
	}, 1000);
});

// then方法和catch方法返回的结果都是promise对象,对象状态由回调函数的执行结果决定(PromiseStatus,PromiseValue)
// 链式调用

p.then(res => {
    console.log(res);
}).catch(err => {
    console.log(err);
})

p.then(
	res => {
		console.log(res);
	},
	err => {
		console.log(err);
	}
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Promise对象的值:

实例对象中的另一个属性【PromiseResult】

保存着对象 【成功/失败】的结果

  • resolve

  • reject

Promise方法:

  • resolve

  • reject

  • all 参数中Promise全部返回成功才成功,返回一个新的Promise

  • race 参数中第一个完成Promise的结果状态就是最终的状态,返回一个新的Promise

Promise异常穿透:

  1. 使用Promise的then链式调用时,可以在最后指定失败的回调。
  2. 前面任何操作出了异常,都会传到最后失败的回调中处理
  3. 想要中断Promise的then链式调用,返回一个pending状态的promise函数
7.1 util.promisify
const fs = require('fs');

// const util = require('util');
// const readFileAsync = util.promisify(fs.readFile);
const { promisify } = require('util');

const readFileAsync = promisify(fs.readFile);

async function read() {
    const content = await readFileAsync('./test.js', 'utf-8');
    return content;
}

read().then(v => {
    console.log(v);
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

7.2 promise自定义封装
class Promise{
    //构造方法
    constructor(executor){
        //添加属性
        this.PromiseState = 'pending';
        this.PromiseResult = null;
        //声明属性
        this.callbacks = [];
        //保存实例对象的 this 的值
        const self = this;// self _this that
        //resolve 函数
        function resolve(data){
            //判断状态
            if(self.PromiseState !== 'pending') return;
            //1. 修改对象的状态 (promiseState)
            self.PromiseState = 'fulfilled';// resolved
            //2. 设置对象结果值 (promiseResult)
            self.PromiseResult = data;
            //调用成功的回调函数
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onResolved(data);
                });
            });
        }
        //reject 函数
        function reject(data){
            //判断状态
            if(self.PromiseState !== 'pending') return;
            //1. 修改对象的状态 (promiseState)
            self.PromiseState = 'rejected';// 
            //2. 设置对象结果值 (promiseResult)
            self.PromiseResult = data;
            //执行失败的回调
            setTimeout(() => {
                self.callbacks.forEach(item => {
                    item.onRejected(data);
                });
            });
        }
        try{
            //同步调用『执行器函数』
            executor(resolve, reject);
        }catch(e){
            //修改 promise 对象状态为『失败』
            reject(e);
        }
    }

    //then 方法封装
    then(onResolved,onRejected){
        const self = this;
        //判断回调函数参数
        if(typeof onRejected !== 'function'){
            onRejected = reason => {
                throw reason;
            }
        }
        if(typeof onResolved !== 'function'){
            onResolved = value => value;
            //value => { return value};
        }
        return new Promise((resolve, reject) => {
            //封装函数
            function callback(type){
                try{
                    //获取回调函数的执行结果
                    let result = type(self.PromiseResult);
                    //判断
                    if(result instanceof Promise){
                        //如果是 Promise 类型的对象
                        result.then(v => {
                            resolve(v);
                        }, r=>{
                            reject(r);
                        })
                    }else{
                        //结果的对象状态为『成功』
                        resolve(result);
                    }
                }catch(e){
                    reject(e);
                }
            }
            //调用回调函数  PromiseState
            if(this.PromiseState === 'fulfilled'){
                setTimeout(() => {
                    callback(onResolved);
                });
            }
            if(this.PromiseState === 'rejected'){
                setTimeout(() => {
                    callback(onRejected);
                });
            }
            //判断 pending 状态
            if(this.PromiseState === 'pending'){
                //保存回调函数
                this.callbacks.push({
                    onResolved: function(){
                        callback(onResolved);
                    },
                    onRejected: function(){
                        callback(onRejected);
                    }
                });
            }
        })
    }

    //catch 方法
    catch(onRejected){
        return this.then(undefined, onRejected);
    }

    //添加 resolve 方法
    static resolve(value){
        //返回promise对象
        return new Promise((resolve, reject) => {
            if(value instanceof Promise){
                value.then(v=>{
                    resolve(v);
                }, r=>{
                    reject(r);
                })
            }else{
                //状态设置为成功
                resolve(value);
            }
        });
    }

    //添加 reject 方法
    static reject(reason){
        return new Promise((resolve, reject)=>{
            reject(reason);
        });
    }

    //添加 all 方法
    static all(promises){
        //返回结果为promise对象
        return new Promise((resolve, reject) => {
            //声明变量
            let count = 0;
            let arr = [];
            //遍历
            for(let i=0;i<promises.length;i++){
                //
                promises[i].then(v => {
                    //得知对象的状态是成功
                    //每个promise对象 都成功
                    count++;
                    //将当前promise对象成功的结果 存入到数组中
                    arr[i] = v;
                    //判断
                    if(count === promises.length){
                        //修改状态
                        resolve(arr);
                    }
                }, r => {
                    reject(r);
                });
            }
        });
    }

    //添加 race 方法
    static race (promises){
        return new Promise((resolve, reject) => {
            for(let i=0;i<promises.length;i++){
                promises[i].then(v => {
                    //修改返回对象的状态为 『成功』
                    resolve(v);
                },r=>{
                    //修改返回对象的状态为 『失败』
                    reject(r);
                })
            }
        });
    }
}   


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184

8. async 函数和 await 表达式

8.1 async 函数
  1. 函数的返回值为 promise 对象
  2. promise 对象的结果由 async 函数执行的返回值决定
8.2 await 表达式
  1. await 右侧的表达式一般为 promise 对象, 但也可以是其它的值
  2. 如果表达式是 promise 对象, await 返回的是 promise 成功的值
  3. 如果表达式是其它值, 直接将此值作为 await 的返回值
  4. await 必须写在 async 函数中, 但 async 函数中可以没有 await
  5. 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
/**
 * resource  1.html  2.html 3.html 文件内容
 */

const fs = require('fs');
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);

//回调函数的方式
// fs.readFile('./resource/1.html', (err, data1) => {
//     if(err) throw err;
//     fs.readFile('./resource/2.html', (err, data2) => {
//         if(err) throw err;
//         fs.readFile('./resource/3.html', (err, data3) => {
//             if(err) throw err;
//             console.log(data1 + data2 + data3);
//         });
//     });
// });

//async 与 await
async function main(){
    try{
        //读取第一个文件的内容
        let data1 = await mineReadFile('./resource/1x.html');
        let data2 = await mineReadFile('./resource/2.html');
        let data3 = await mineReadFile('./resource/3.html');
        console.log(data1 + data2 + data3);
    }catch(e){
        console.log(e.code);
    }
}

main();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/259724
推荐阅读
相关标签
  

闽ICP备14008679号