相比传统的回调函数的方式,Promise最大的优势是可以链式调用,可以最大程度的避免回调嵌套。
- Promise对象的then方法返回的是一个全新的Promise对象。
- 后面的then方法就是在为上一个then返回的Promise注册回调。
- 前面then方法中回调函数的返回值会作为后面then方法回调的参数。
- 如果回调中返回的是Promise,那后面then方法的回调会等待它的结束。
var promise = ajax("user.json");
var promise2 = promise.then(
function onFulfilled(value) {
console.log("onFulfilled", value);
},
function onRejected(error) {
console.log("onRejected", error);
}
);
console.log(promise == promise2);
then的内部也会返回一个Promise对象,但不等于原来的对象,所以说这里的链式调用并不是常见的在方法内部去返回this的方式实现的
Promise异常处理
Promise调用结果一旦失败就会在调用在then方法中调用onRejected回调函数,如果在执行中出现了异常或者手动抛出异常,也会调 用onRejected回调函数
function ajax(url) {
return new Promise(function(resolve, reject) {
throw new Error();//手动抛出一个异常
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "json";
xhr.onload = function() {
if (this.status == 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
xhr.send();
});
}
ajax("user.json").then(
function onFulfilled(value) {
console.log("onFulfilled", value);
},
function onRejected(error) {
console.log("onRejected", error);
}
);
可以用catch方法去注册onRejected回调,其实就是then方法的一个别名
因为每个then方法都会返回一个全新的Promise对象,通过链式调用的方式调用的catch实际上在给前面then方法返回的Promise对象指定失败的回调,并不是直接给第一个Promise对象所指定的,只不过因为这是同一个Promise的链条,前面Promise的异常会一直往后传递,所以在上面例子catch里才能够捕获到第一个Promise当中的异常。catch其实是then的第二个参数
ajax("user11.json")//访问一个不存在的地址也会捕获到上面手写的异常
.then(function onFulfilled(value) {
console.log("onFulfilled", value);
})
.catch(function onRejected(error) {
console.log("onRejected", error);
});
除次之外还可以在全局对象上注册一个unhandledrejection事情处理一些代码中没有被手动捕获的promise异常
这是在全局中注册的捕获异常的方法,不推荐使用,还是要在代码中明确捕获每一个可能的异常
Promise静态方法
Promise.resolve():快速的把一个值转成promise对象,比如
Promise.resolve('foo').then(function(value){
console.log(value);
});
会直接返回一个状态是foo的成功的promise对象。等价于
new Promise(function(resolve,reject){
resolve("foo");
});
Promise.reject():快速状态一个一定是失败的的Promise对象,不管传入什么样的数据都是Promise失败的原因
Promise.reject(new Error('rejected')).catch(function(error){
console.log(error);
});
Promise并行执行
如果在一个页面中经常要请求一个接口的情况,如果这些请求相互之间没有什么依赖,最好的选择是同时去请求它们,这样避免一个个依次去请求会消耗更多的时间。
如何知道所有的请求任务都结束了呢?传统的方法会有一个计数器,每调用成功一个就累加一下。这种方法比较麻烦,还要考虑出现异常的情况。
Promise的all接收的是一个数组,数组里每一个都是promise对象。all方法会返回一个全新的promise对象,当所有的请求都完成后,这个promise对象才会完成,then方法里返回一个数组,这个数组里包含着每个异步任务执行的结果
var promise = Promise.all([ajax("users.json"), ajax("users1.json")]);
promise
.then(function(values) {
console.log(values);
})
.catch(function(error) {
console.log(error);
});
只有当这两个请求任务都成功结束了,这个新的promise才会成功结束,若其中一个任务失败,这个promise就会失败。
Promise.race()同样可以把多个promise对象组合成一个全新的promise对象。
Promise.all()是等待所有任务结束才会结束,而race()只会等待第一个结束的任务一起结束
Promise的执行时序/宏任务vs微任务
console.log("global start");
setTimeout(() => {
console.log("setTimeout");
}, 0);
Promise.resolve()
.then(() => {
console.log("promise1");
})
.then(() => {
console.log("promise2");
})
.then(() => {
console.log("promise3");
});
console.log("global end");
先打印了promise再打印了settimeout,因为promise异步执行时序会有点特殊。promise的回调会作为微任务执行。
宏任务和微任务的区别
回调队列中的任务称之为“宏任务”,宏任务执行过程中可以临时加上一些额外需求,可以选择作为一个新的宏任务进到队列中排队,也可以作为当前任务的“微任务”是指直接在当前任务结束过后立即执行,而不是到整个队伍的末尾重新排队。
微任务是为了提高整体的响应能力。目前绝大多数异步调用都是作为宏任务执行的,而promise&MutationObserver对象,还有node端有一个process.nextTick是微任务,直接在本轮调用的末尾就执行了