ES 6+ 异步请求
一个真正的网站,除了能够展示渲染信息外,还应当实时更新数据,客户端无法收集信息,收集数据是通过服务端处理的,客户端就需要通过请求的方式到服务端拿到收集的数据
杰森数据
JSON: JavaScript Object Notation (JavaScript 对象标识法),是存储和交换文本信息的语法,类似于 XML,但 JSON 比 XML 更小、更快、更易解析。
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。
- JSON 具有自我描述性,更易理解
与 XML 相同处 | 与 XML 不同处 |
---|---|
XML 与 JSON 都是纯文本 | XML 有结束标签,JSON 没有结束标签 |
XML 与 JSON 都具有 “自我描述性” | JSON 比 XML 代码更短 |
XML 与 JSON 都具有层级结构 | JSON 比 XML 读写的速度更快 |
XML 与 JSON 都可以被 JavaScript 进行解析 | JSON 能够使用 JavaScript 的 eval() 方法进行解析,而 XML 不能使用 eval() |
XML 与 JSON 都可以使用 AJAX 进行传输 | JSON 不能使用 JavaScript 保留字,而 XML 可以使用 JavaScript 保留字 |
JSON 文件的文件类型是 “.json”,文本的 MIME 类型是 “application/json”
对象基础
{
"name": "tom",
"age": 12
}
--------------------------------------------------------------------------------------------------------------------------------
{
"disc": 'miao',
"data": [
{ "name": "tom", age: 12 },
{ "name": 'jim', age: 11 }
]
}
应用场景
- JSON.parse() :将服务器返回的字符串格式数据转化为 JavaScript 的对象格式,不能直接解析日期格式,遇到日期类型需要通过实例化得到日期
- JSON.stringify():将 JavaScript 的对象格式转化为字符串格式数据发送到服务器,不能将日期格式数据转换,遇到日期类型数据线转换为字符类型
XMLHttpRequest
XMLHttpRequest 是浏览器的内置对象,通过这个对象,我们能向服务器发送请求和接收响应,实现数据交换。
很多 AJAX 底层都是通过 XMLHttpRequest 对象实现
可以通过构造函数 XMLHttpRequest()
创建一个 XMLHttpRequest 对象
XMLHttpRequest 对象常用的属性如下:
-
readyState
:只读属性,表示请求的当前状态若为
0
,表示已经生成请求实例,但未调用open()
方法若为
1
,表示已经调用open()
方法,但未调用send()
方法若为
2
,表示已经调用send()
方法,且已收到服务器返回的头信息若为
3
,表示正在接收服务器返回的数据体若为
4
,表示服务器返回的数据已全部接收完毕,或者发生错误 -
response
:只读属性,表示服务器返回的数据,其类型由responseType
的值决定 -
responseType
:定义返回数据的类型,它的值是一个字符串若为
'text'
或''
,表示服务器返回文本数据若为
'json'
,表示服务器返回 Json 对象若为
'blob'
,表示服务器返回 Blob 对象若为
'arraybuffer'
,表示服务器返回 ArrayBuffer 对象若为
'document'
,表示服务器返回 Document 对象 -
responseText
:只读属性,表示服务器返回的文本数据,如果请求失败则为 null -
responseXML
:只读属性,表示服务器返回的 Document 对象,如果请求失败则为 null -
responseURL
:只读属性,表示返回数据的服务器的网址 -
status
:只读属性,表示 HTTP 状态代码 -
statusText
:只读属性,表示 HTTP 状态信息 -
timeout
:定义请求的超时时间,若请求超过该时间,则自动结束此请求 -
withCredentials
:一个布尔类型的值,表示在跨域请求时是否带有授权信息
XMLHttpRequest 对象常用的方法如下:
-
open()
:初始化请求,它接收以下五个参数参数
method
是一个字符串,表示将要使用的请求方法参数
url
是一个字符串,表示发送请求的目标地址参数
async
是一个布尔值,表示请求是否需要异步进行,默认为 true参数
user
是一个字符串,表示用于认证的用户,默认为空字符串参数
password
是一个字符串,表示用于认证的密码,默认为空字符串 -
setRequestHeader()
:设置请求头,必须在open()
之后send()
之前调用 -
overrideMimeType()
:将服务器返回的数据解析成指定类型,必须在open()
之后send()
之前调用 -
send()
:发送请求,可以在参数中带上请求体请求体的类型可以是
null
、String
、Blob
、ArrayBuffer
、Document
、FormData
-
abort()
:中止请求 -
getAllResponseHeaders()
:获取所有响应头 -
getResponseHeader()
:获取指定响应头
XMLHttpRequest 对象常用的事件如下:
-
readystatechange
:readyState
发生变化时触发 -
loadstart
:当请求开始时触发(发出 HTTP 请求) -
loadend
:当请求结束时触发(请求成功或者失败) -
load
:当请求成功时触发 -
error
:当请求错误时触发 -
abort
:当请求中止时触发 -
timeout
:当请求超时时触发 -
progress
:监听上传和下载进度,该事件对应的事件处理函数带有一个事件对象,该事件对象有三个属性属性
loaded
表示已经传输的数据量,属性total
表示总数据量属性
lengthComputable
表示加载进度是否可以计算,是一个布尔类型的值
这里需要注意,下载触发的是 xhr
对象的 progress
事件,上传触发的是 xhr.upload
对象的 progress
事件
(1)发送 GET 请求
var xhr = new XMLHttpRequest();
xhr.responseType = 'text';
xhr.open('GET', 'http://www.httpbin.org/get');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.onerror = function() {
console.log('error');
};
xhr.send();
(2)发送 POST 请求
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://www.httpbin.org/post');
xhr.onload = function() {
console.log(xhr.response);
};
xhr.onerror = function() {
console.log('error');
};
var data = new FormData();
data.append('username', 'admin');
data.append('password', '12345');
xhr.send(data);
(3)接收图片
var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png');
xhr.onloadstart = function() {
xhr.responseType = 'blob';
};
xhr.onload = function() {
if (this.status === 200) {
var blob = xhr.response;
var img = document.createElement('img');
img.onload = function() { window.URL.revokeObjectURL(img.src); };
img.src = window.URL.createObjectURL(blob);
document.getElementById('container').appendChild(img);
}
};
xhr.send();
(4)监听进度
var xhr = new XMLHttpRequest();
xhr.open('GET', '/download/or/upload');
// 用于监听下载进度
xhr.onprogress = progressHandler;
// 用于监听上传进度
xhr.upload.onprogress = progressHandler;
function progressHandler(e) {
if (e.lengthComputable) {
console.log(e.loaded / e.total);
} else {
console.log('无法获取进度');
}
}
xhr.send();
阿贾克斯
Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术,Ajax 不是新的编程语言,是一种使用现有标准的新方法,用于网页局部刷新处理
- 运用 XHTML + CSS 来表达资讯;
- 运用 JavaScript 操作 DOM 来执行动态效果渲染;
- 运用 XML 和 XSLT 操作资料;
- 运用 XMLHttpRequest 或新的 Fetch API 与网页服务器进行异步资料交换;
在桌面新建一个 api 文件夹,双击打开,在窗口路径地方。输入 cmd 加回车,打开 Dos 窗口,输入 npm init -y + 回车,生成一个 package.json 配置文件
继续在 Dos 窗口输入:npm install express –save 安装 express 模块,再打开的 api 文件夹下新建 app.js 文件,编辑如下代码,Dos 输入node app.js 回车,输出:Example app listening at http://127.0.0.1:3000 标识启动成功,Node 环境的安装配置参考:开发工具 —— 前端工程开发环境 Node 之 NVM 管理应用
let app = require('express')();
// 设置跨域访问
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By", ' 3.2.1');
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
// get 请求接口
app.get('/getlist', function(req, res) {
res.status(200);
res.json([{name: '张三'}, {name: '李四'}]);
});
// post 请求接口
app.post('/postForm', function(req, res) {
res.status(200);
res.json({ name: 'zhangsan', pass: '123456' });
});
let server = app.listen(3000, 'localhost', function() {
let host = server.address().address;
let port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
同样在桌面上新建 request 文件夹,在 request 文件夹下新建 index.js 和 index.html ,并编辑代码如下,运行结果如下图所示结果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<title>原生AJAX插件</title>
</head>
<body>
<button id="btn-get">GET 请求</button>
<button id="btn-post">POST 请求</button>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>
--------------------------------------------------------------------------------------------------------------------------------
let Ajax = {
newAjax: function(options) {
let ajax = {};
/**
* @param {[Object]} ajax.options [声明请求的参数对象]
* @param {[String]} ajax.options.method [声明请求接口请求方式:GET、POST,默认 GET]
* @param {[String]} ajax.options.url [声明请求接口接口的地址]
* @param {[Object]} ajax.options.data [声明请求接口的提交数据,POST 方式请求使用]
* @param {[Number]} ajax.options.timeout [声明请求接口的总时]
* @param {[Function]} ajax.options.success [声明请求接口成功回调函数]
* @param {[Function]} ajax.options.error [声明请求接口失败回调函数]
* @param {[XMLHttpRequest]} ajax.xhr [声明一个请求对象]
*/
ajax.options = options || {};
ajax.options.method = options && options.method.toUpperCase() || 'GET';
ajax.options.url = options && options.url || '';
ajax.options.data = options && options.data || {};
ajax.options.timeout = options && options.timeout || 8000;
ajax.options.success = options && options.success || function() {};
ajax.options.error = options && options.error || function() {};
ajax.xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
// 如果为 GET 请求方式,参数需要提前拼接在地址上,引用 ? 拼接,且参数格式:name=zhangsan&pass=123456
if(ajax.options.method === 'GET') {
ajax.xhr.open("GET", ajax.options.url, true);
ajax.xhr.send(null);
}
// 如果为 POST 请求方式,需要将数据 send 发送给请求
if(ajax.options.method === 'POST') {
ajax.xhr.open("post", ajax.options.url, true);
ajax.xhr.send(ajax.options.data);
}
// 设置一个请求计时,请求超出请求时间,终止请求
setTimeout(function(){
if(ajax.xhr.readySate != 4) {
ajax.xhr.abort();
}
}, ajax.options.timeout);
// 请求结果判断,如果状态属于如下标识表示请求成功,执行成功回调,否则执行失败回调
ajax.xhr.onreadystatechange = function() {
if(ajax.xhr.readyState === 4) {
let status = ajax.xhr.status;
if(status >= 200 && status < 300 || status == 304){
ajax.options.success && ajax.options.success(ajax.xhr.responseText, ajax.xhr.responseXML);
}else{
ajax.options.error && ajax.options.error(status);
}
}
};
return ajax;
}
};
window.onload = function() {
document.getElementById('btn-get').onclick = function() {
let ajax = Ajax.newAjax({
method: 'GET',
url: 'http://localhost:3000/getlist',
success: function(res) {
console.log(res);
},
error: function(err) {
console.log(err);
}
});
};
document.getElementById('btn-post').onclick = function() {
let ajax = Ajax.newAjax({
method: 'POST',
url: 'http://localhost:3000/postForm',
data: { name: 'zhangsan', pass: '123456' },
success: function(res) {
console.log(res);
},
error: function(err) {
console.log(err);
}
});
};
};
同步异步区别
- 同步:是指两个或两个以上的事务在变化的过程中保持一定的相对关系,确保一个完成接着下一个的顺序,去执行事务操作
- 异步:是指两个或两个以上的事务在变化的过程中保持互不干涉的关系,你执行你的,我执行我的的原则,去执行事务操作
请求方式
- GET 请求:通过地址传递数据,数据量偏小,需要编码处理,请求会缓存到浏览器中,安全性低
- POST请求:通过实体传递数据,数据量无限制,数据有格式,变动性访问,安全性高
属性 | 描述 |
---|---|
responseText | 获得字符串形式的响应数据 |
responseXML | 获得 XML 形式的响应数据 |
属性 | 描述 |
---|---|
onreadystatechange | 存储函数(或函数名),每当 readyState 属性改变时,就会调用该函数 |
readyState | 存有 XMLHttpRequest 的状态,从 0 到 4 发生变化,0: 请求未初始化1: 服务器连接已建立2: 请求已接收3: 请求处理中4: 请求已完成,且响应已就绪 |
status | 200: “OK”、404: 未找到页面 |
异步编程
Promise 是异步编程的一种解决方案,比传统的回调函数和事件解决方案更合理、更强大,配合 ES6 语法标准,统一用法,并原生提供了 Promise 对象
- 对象的状态不受外界影响:Pending 状态(进行中)、Fulfilled 状态(已成功)、Rejected 状态(已失败)
- 一旦状态改变就不会再变:Pending = Fulfilled,Pending = Rejecte
function greet(){
let promise = new Promise(function(resolve,reject) {
let greet = "hello world";
resolve(greet);
});
return promise;
}
greet().then(function(v) {
console.log(v);
});
// hello world
--------------------------------------------------------------------------------------------------------------------------------
function greet(){
let promise = new Promise(function(resolve,reject) {
let greet = "hello world";
resolve(greet);
});
return promise;
}
greet().then(function(v) {
console.log(v + 1);
return v;
}).then(function(v) {
console.log( v + 2);
return v;
}).then(function(v) {
console.log(v+3);
});
// hello world1
// hello world2
// hello world3
--------------------------------------------------------------------------------------------------------------------------------
function judgeNumber(num){
let promise = new Promise(function(resolve, reject) {
let num = 5;
if(num < 5) {
resolve("num 小于 5,值为:" + num);
} else {
reject("num 不小于 5,值为:" + num);
}
});
return promise;
}
judgeNumber().then(function(message) {
console.log(message);
}, function(message) {
console.log(message);
});
// num 不小于 5,值为:5
--------------------------------------------------------------------------------------------------------------------------------
function judgeNumber(num){
let promise = new Promise(function(resolve, reject) {
let num = 5;
if(num < 5) {
resolve("num 小于 5,值为:" + num);
} else {
reject("num 不小于 5,值为:" + num);
}
});
return promise;
}
judgeNumber().then(function(message) {
console.log(message);
}).catch(function(err) {
console.log(err);
});
// num 不小于 5,值为:5
--------------------------------------------------------------------------------------------------------------------------------
function p1(){
var promise1 = new Promise(function(resolve,reject){
console.log("p1的第一条输出语句");
console.log("p1的第二条输出语句");
resolve("p1完成");
});
return promise1;
}
function p2(){
var promise2 = new Promise(function(resolve,reject){
console.log("p2的第一条输出语句");
setTimeout(()=>{console.log("p2的第二条输出语句");resolve("p2完成")},2000);
});
return promise2;
}
function p3(){
var promise3 = new Promise(function(resolve,reject){
console.log("p3的第一条输出语句");
console.log("p3的第二条输出语句");
resolve("p3完成")
});
return promise3;
}
Promise.all([p1(),p2(),p3()]).then(function(data){
console.log(data);
});
// p1的第一条输出语句
// p1的第二条输出语句
// p2的第一条输出语句
// p3的第一条输出语句
// p3的第二条输出语句
// p2的第二条输出语句
// ["p1完成", "p2完成", "p3完成"] 全部执行完成后打印结果
--------------------------------------------------------------------------------------------------------------------------------
function p1(){
var promise1 = new Promise(function(resolve,reject){
console.log("p1的第一条输出语句");
console.log("p1的第二条输出语句");
resolve("p1完成");
});
return promise1;
}
function p2(){
var promise2 = new Promise(function(resolve,reject){
console.log("p2的第一条输出语句");
setTimeout(()=>{console.log("p2的第二条输出语句");resolve("p2完成")},2000);
});
return promise2;
}
function p3(){
var promise3 = new Promise(function(resolve,reject){
console.log("p3的第一条输出语句");
console.log("p3的第二条输出语句");
resolve("p3完成")
});
return promise3;
}
Promise.race([p1(),p2(),p3()]).then(function(data){
console.log(data);
});
// p1的第一条输出语句
// p1的第二条输出语句
// p2的第一条输出语句
// p3的第一条输出语句
// p3的第二条输出语句
// p1完成 完成一条就不等待,异步
// p2的第二条输出语句
步编程的优缺点
优点 | 缺点 |
---|---|
解决回调 | 无法监测进行状态 |
链式调用 | 新建立即执行且无法取消 |
减少嵌套 | 内部错误无法抛出 |
回调地狱
通常解决回调地狱的问题,最开始使用的是 ES 6 中的 Promise
,直译过来就是在未来某一个时间点承诺返回数据给你
Promise 构造函数有两个变量 resolve 用于返回异步执行成功的函数 reject 用于返回异步执行失败的函数。配合 then 与 catch 一起使用
function greet(){
let promise = new Promise(function(resolve,reject) {
let greet = "hello world";
resolve(greet);
});
return promise;
}
greet().then(function(v) {
console.log(v + 1);
return v;
}).then(function(v) {
console.log( v + 2);
return v;
}).then(function(v) {
console.log(v+3);
});
使用过多的 then 也会给程序造成一定的问题,其中存在一个失效的话,就导致后续的程序崩溃,那么就需要 ES 6 中的 另一个 Generator 方式
Generator
(生成器)是一种有效利用内存的机制,一边循环一边计算生成数值的机制。通过配合Promise可以更加优雅的写异步代码
import { take, fork, call } from 'redux-saga/effects';
import { FETCH_WHOME_SEAN } from './action';
import { getWhomePaneSean } from './server';
function* fetchWhomeSean() {
while (true) {
const { callback } = yield take(FETCH_WHOME_SEAN);
// 异步调用接口,传入参数,获取版面初始化通告数据
const response = yield call(getWhomePaneSean);
response.code === 200 && callback(response.data);
}
}
export default [
fork(fetchWhomeSean),
];
generator 作为 ES6 中顶尖的难懂的东西,在日常开发的出镜率其实也挺高的,除去 redux-saga 不说,基于 generator 封装的 async 和 await 也是开发中的重要利器。
generator 这里看成 async-await 作用类似,在 redux-saga 讲解其应用方式
async 函数,就是 Generator 的语法糖,只是写法不一样而已
function greet(){
let promise = new Promise(function(resolve,reject) {
let greet = "hello world";
resolve(greet);
});
return promise;
}
async function persition() {
const v = await greet();
console.log(v + 1);
console.log(v + 2);
console.log(v + 3);
}
persition();
async/await 与 generator 优势
- async/await 是内置执行器,而 generator 执行必须依靠执行器
- async/await 相较于 */yield 更加语义化
- async/await 相较于 generator 具有更广的适用性
- async/await 比 generator 返回的 Iterator 对象更方便
- 如果 generator 被监听器包裹执行的话与 async/await 是一样的