异步
如何表达和控制持续一段时间的程序行为,这不仅仅是指从for循环开始到结束的过程,当然这也需要持续一段时间(几微秒或几毫秒)才能完成。它是指程序的一部分现在运行,而另一部分则在将来运行——现在和将来之间有段间隙,在这段间隙中,程序没有活跃执行。程序中现在运行的部分和将来运行的部分之间的关系就是异步编程的核心。
其中,主要的异步代码有 ajax、定时器、异步控制台(console*方法族),而这里介绍ajax技术
ajax技术
1、什么是ajax?
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
ajax是很多技术的集合。项技术的包括,
1、浏览器的xmlHTTPRequest对象。负责开通连接通道,可以传递消息。
2、JavaScript。负责调用 xmlHTTPRequest对象进行与后台交互的媒介。
3、xml。用于服务器应答的传递信息的数据格式,除了xml外,还可以使用任何文本格式,如text、html,json等。
xmlHTTPRequest对象属性
onreadystatechange 每次状态改变所触发事件的事件处理程序。
responseText 从服务器进程返回数据的字符串形式。
responseXML 从服务器进程返回的DOM兼容的文档数据对象。
status 从服务器返回的数字代码,比如常见的404(未找到)和200(已就绪)
status Text 伴随状态码的字符串信息
readyState 对象状态值
0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法)
1 (初始化) 对象已建立,尚未调用send方法
2 (发送数据) send方法已调用,但是当前的状态及http头未知
3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误,
4 (完成) 数据接收完毕,此时可以通过通过responseXml和responseText获取完整的回应数据
2、一个简单的ajax实例
伪代码:
1、先创建一个XMLHttpResquest对象
2、请求发送到服务器。使用 XMLHttpRequest 对象的 open() 和 send() 方法
3、响应。如需获得来自服务器的响应,请使用 XMLHttpRequest 对象的 responseText 或 responseXML 属性
4、状态改变,处理响应。当请求被发送到服务器时,我们需要执行一些基于响应的任务。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息
function axja(cb) {
var xmlhttp;
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
}
else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function () {
//readyState为4,代表数据发送完毕;status为200时,代表请求成功,一切已经就绪。
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
//处理响应
cb()
}
}
xmlhttp.open("GET", url , true);
xmlhttp.send();
}
}
JS 异步解决方案的发展历程以及优缺点
1、回调函数
缺点:
1、因为层层嵌套,会出现回调地狱的情况。
2、缺乏顺序性: 回调地狱导致的调试困难,和大脑的思维方式不符;
3、嵌套函数存在耦合性,一旦有所改动,就会牵一发而动全身,即(控制反转);
4、嵌套函数过多的多话,很难处理错误。
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行)。
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
2、Promise
Promise 就是为了解决 callback 的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装。
优点:解决了回调地狱的问题。
缺点:无法取消 Promise ,错误需要通过回调函数来捕获。
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
3、Generator
特点:可以控制函数的执行,可以配合 co 函数库使用。
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
4、asyn await
async、await 是异步的终极解决方案。
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题;
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
}