写作原因
传统解决异步的解决方法有两种:
- 借助回调函数
- 依赖于事件 => 自定义事件 => 请求成功时派发(数据存储到事件对象中)
传统解决方法代码实现
- 借助回调函数解决异步
var xhr = new XMLHttpRequest();
xhr.open("get", "../data/goods.json", true);
xhr.send();
// 绑定监听事件 => 页面加载时,只做绑定 => 当xhr.readyState 改变时触发
xhr.onreadystatechange = function (callback) {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
result = JSON.parse(result);
// 向事件目标派发事件
if (callback && typeof callback == 'function') {
callback(result);
}
}
}
- 自定义实践解决异步
// 创建事件目标 => 绑定自定义事件
var oTarget = new EventTarget();
oTarget.addEventListener("success", function (e) {
console.log("请求成功", e.data);
})
var xhr = new XMLHttpRequest();
xhr.open("get", "../data/goods.json", true);
xhr.send();
// 绑定监听事件 => 页面加载时,只做绑定 => 当xhr.readyState 改变时触发
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
result = JSON.parse(result);
// 向事件目标派发事件
var e = new Event("success");
e.data = result;
oTarget.dispatchEvent(e);
}
}
Promise的产生背景—回调函数噩梦
回调函数噩梦(恐怖回调),也被称为恶魔金字塔,例如ajax依赖调用时,回调函数会层层嵌套,而这种层层嵌套的写法,往往会让人难以理解,所以称之为噩梦。
例如:服务器中有3个txt文件,我们需要在html上,通过js中的异步的ajax,分别获取这3个文件的内容,假设这3个文件分别存储的数据为1、2、3,那么我希望在js中,能够求出1+2+3,把6输出。
你觉得代码应该怎么写?
var str = "";
$.ajax({
type: "get",
url: "../data/1.txt",
success: function (msg1) {
console.log(msg1);
str += msg1;
setTimeout(function () {
$.ajax({
type: "get",
url: "../data/2.txt",
success: function (msg2) {
console.log(msg2);
str += msg2;
$.ajax({
type: "get",
url: "../data/3.txt",
success: function (msg3) {
console.log(msg3);
str += msg3;
console.log(str);
}
})
}
})
}, 10)
}
})
promise对象
Promise是一种思维方式,是一种解决回调函数噩梦的方案(Promise 是异步编程的一种解决方案)。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
Promise对象代表一个异步操作,有三种状态:
- Pending(进行中)
- Resolved(已完成,又称Fulfilled)
- Rejected(已失败)
注:可以把Promise看成是状态机,当该Promise对象创建出来之后,其状态就是进行中,然后通过程序来控制到底是执行已完成,还是执行已失败。因为Promise处理的是异步任务,所以我们还得对Promise做监听,当Promise的状态发生变化时,我们要执行相应的函数(then)。
Promise的特点:
-
对象的状态不受外界影响 => 想要改变状态 需要借助 resolve,rejected 方法
resolve() => pending(进行中状态) => fulfilled(已成功)
reject() => pending(进行中状态) => rejected(已失败) -
Promise 对象 的状态一经改变 不能再次改变
Promise的优点
- 有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
Promise的缺点
- 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
- 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。(直接报错)
- 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise实例化代码实现
var promise = new Promise( function( resolve, reject ){
// 通常这里写一个异步任务,在这个异步任务中,
// 通过resolve或reject来改变promise的状态。
resolve(“数据”);
// resolve指将状态更改为已完成
// reject指将状态更改为已失败。
} );
// then实际上是对promise的状态监听
// 当状态为已完成时,触发第一个参数函数。
// 当状态为已失败时,触发第二个参数函数(第二个参数函数,可写可不写)
// 每一个函数中的参数,指当初状态改变时,所保存的数据。
promise.then(function(str){}, function(err){});
// or
// catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。(注 Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。)
promise.then(function(str){}).catch(function(err){})
总结
其实promise对象的本职就是一个封装的比较好的回调函数,另外在promise的then_catch的链式调用上来讲:
-
链式调用过程中,如果回调函数的返回值 是非Promise对象then方法的链式调用,依次指定了多个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数, 依此类推, catch可以捕获链式操作过程中所有的错误(只要链式调用过程中 有一个出错 都会被catch捕获)
成功的可以更成功,一失足成千古恨 -
链式调用过程中,如果回调函数的返回值 是Promise对象 (插队)then方法的链式调用,依次指定了多个回调函数。第一个回调函数完成以后 如果返回值是一个Promise对象,后续的回调函数会等待该Promise对象状态改变之后(成功就走then 失败=>catch),再继续调用和传递数据