八:以理论结合实践方式梳理前端 ES 6+ ——— ES 6+ 异步请求

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():发送请求,可以在参数中带上请求体

    请求体的类型可以是 nullStringBlobArrayBufferDocumentFormData

  • abort():中止请求

  • getAllResponseHeaders():获取所有响应头

  • getResponseHeader():获取指定响应头

XMLHttpRequest 对象常用的事件如下:

  • readystatechangereadyState 发生变化时触发

  • 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: 请求已完成,且响应已就绪
status200: “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 是一样的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值