Promise 是ES6对异步编程的一种解决方案,比传统的解决方案(如回调函数和事件)更合理更强大.
Promise 简单说就是一个容器,里面保存着一个尚未完成且预计在未来完成的异步操作
Promise是一个构造函数,用来创建一个Promise对象.
var promise=new Promise();
Promise对象代表一个异步操作,有三种状态
改变Promise对象的状态,有两种可能:
回调函数一个嵌套一个,先执行这个外层的,然后在里层一层一层的去执行
$.ajax({
type: "get",
url: '',
data: {},
success: function () {
$.ajax({
type: "get",
url: '',
data: {},
success: function () {
$.ajax({
type: "get",
url: '',
data: {},
success: function () {
$.ajax({
type: "get",
url: '',
data: {},
success: function () {
}
});
}
});
}
});
}
});
看上面代码,恶不恶心?但是以前我们必须使用这种方式才能得到我们想要的.
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,解决了回调地狱!
const promise = new Promise(function (reslove, reject) {
if(/*异步操作成功*/){
reslove('成功');//从pending变为fulfilled
}
else{
reject('失败');//从pending变为rejected
}
});
Promise是一个构造函数,接受一个函数作为参数,被称为执行器(executor),该函数的两个参数分别是reslove和reject
reslove和reject是两个函数,由Javascript引擎提供,不用自己部署
reslove函数的作用是,将Promise对象的状态从"进行中"变成"成功"(即从pending变为resloved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
reject 函数的作用是,将Promise对象的状态从"进行中"变为"失败"(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(function (value) {
//success
}, function () {
//error
});
Promise实例的then方法:
最后返回的是一个新的Promise实例
Promise的实例方法catch:用于指定发生错误时的回调函数
promise.catch(function (msg) {
console.log(msg);
});
在Promise对象中:
如果该对象状态变为
resolved,则会调用then()方法
如果该对象状态变为rejected,也就是异步操作抛出错误.则会调用catch()方法,另外,在then()里面抛出的异常,也会被catch()捕获
如果Promise状态已经变成resolved,再抛出异常错误是无效的
const promise = new Promise(function (reslove, reject
reslove('成功');
throw new Error();
});
promise.then(function (msg) {
console.log(msg);
//success
}, function (msg) {
console.log(msg);
//error
});
promise.catch(function (msg) {
console.log(msg);
});
上述代码,Promise在resolve语句后面,再抛出异常,不会被捕获,因为Promise的状态一旦改变,就永久保持该状态.
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止.所以,有这样的冒泡性质,就不需要在每个Promise对象中单独捕获异常了.
Promise.prototype.catch = function(fn){
return this.then(null,fn);
}
其实catch的本质还是then()这个方法,只不过进行了封装,一个语法糖而已.
Promise.prototype.catch()
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名
then()第二个参数:Promise进入失败状态,或内部执行错误
catch()可以捕捉到进入成功状态的函数内部错误
const promise = new Promise(function (reslove, reject)
throw new Error();
reslove('成功');
});
promise.then(function (msg) {
console.log(msg);
//success
}, function (msg) {
console.log("then()第二个函数",msg);
//error
}).catch(function (msg) {
console.log("catch方法",msg);
});
上面这段代码,我在Promise中抛出一个错误
then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。
const promise = new Promise(function (reslove, reject) {
throw new Error();
reslove('成功');
});
promise.then(function (msg) {
console.log(msg);
//success
}).catch(function (msg) {
console.log("catch方法",msg);
});
如果then()的第二个参数不存在,则会直接被catch()捕获!
当then()的第二个参数和catch同时存在时,还取决于Promise内部:
如果是reject抛出错误,只有then第二个参数能捕获
如果是resovle抛出成功,那么then的第二个参数不能捕获到,只有catch()才能捕获到!
const promise = new Promise(function (reslove, reject) {
//reslove('成功');
//reject('失败');
});
promise.then(function (msg) {
throw new Error();
console.log(msg);
},function(msg){
console.log("then方法",msg);
}).catch(function (msg) {
console.log("catch方法",msg);
});
为什么说要推荐使用catch()方法,不推荐使用then()方法的第二个函数呢?因为使用catch()方法可以捕获到前面then()方法里执行中的错误,也更接近同步的写法(try/catch),所以,建议总是使用catch()方法,而不是使用then()的第二个参数
promise
.then(function (data) {
// success
}, function (err) {
// error
});
promise
.then(function (data) {
// success
})
.catch(function (err) {
// error
});
finally()方法用于指定不管Promise对象最后状态如何,都会执行的操作
该方法是ES2018引入标准的
promise.finally(()=>{});
这个finally
方法,不管promise
的最后状态,在执行完then()
或catch
指定的回调函数以后,都会执行finally
方法里指定的回调函数
server.listen(port)
.then(function(){
//....
})
.finally(server.stop);
Promise是用来管理异步编程的,它本身不是异步的
var promise = new Promise(function(resolve, reject) {//这里是同步任务
console.log(1);
setTimeout(function(){
console.log(2);
},0);
resolve();
})
promise.then(function() {//这里是异步任务
console.log(3);
})
console.log(4); //打印 1,4,3,2
至于为什么为打印1,4,3,2,可以参考事件循环 Event Loop
将所有的异步,变成同步执行!
需求:
let status = 0;
new Promise(function (reslove, reject) {
setTimeout(() => {
if (status == 0) {
console.log("2秒过后吃饭");
reslove("成功");
}
else {
reject("失败");
}
}, 2000);
}).then(function (msg) {
console.log("2秒到了" + msg);
return new Promise(function (reslove, reject) {
setTimeout(() => {
console.log("3秒过后做作业");
reslove("成功");
}, 3000);
});
}).then(function (msg) {
console.log("3秒到了" + msg);
return new Promise(function (reslove, reject) {
setTimeout(() => {
console.log("7秒过后上厕所");
reslove("成功");
}, 7000);
});
}).then(function (msg) {
setTimeout(() => {
console.log("喝茶");
}, 1000);
}).catch(function (msg) {
console.log("失败了");
});
//2秒过后吃饭
//2秒到了成功
//3秒过后做作业
//3秒到了成功
//7秒过后上厕所
//喝茶
上面的几个过程中,无论哪个对象里面抛出异常,到最后,都可以通过catch()来捕捉,通过这种方式可以将所有 Promise 对象的错误合并到一个函数来处理,这样就解决了每个任务都需要单独处理异常的问题。
链式调用,大家可以想到jQuery,jQuery中之所以能够支持链式调用,大家都知道,每个方法最后都会
return this
,将当前的对象return出去,而咱们的Promise稍微有点不一样
Promise支持链式调用的原理是:每次对Promise调用then方法之后,都会创建并会返回一个新的Promise对象,可以继续用then方法调用
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数,此外,Promise对象提供统一的接口,使得控制异步操作更加容易
1.无法取消Promise,一旦新建它,就会立即执行,无法中途取消,
2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部.
3.当处于Pending状态时,无法得知目前进展到哪一个阶段
针对缺点的第1点,我们可以使用在Promise外层包一层函数,类似于懒加载的原理,需要才加载它,使用它.
function getPromise(){
return new Promise(function(resolve,reject){
resolve('成功');
});
}
var promise=getPromise();
promise.then(function(msg){
console.log(msg)
});
Promise.all()是一个静态方法,属于Promise构造函数的方法
Promise.all可以将多个Promise实例包装成一个新的Promise实例
const p = Promise.all([p1, p2, p3]);
短路特性
). function execPromise() {
var p1 = new Promise(function (res
reslove('成功p1');
});
var p2 = new Promise(function (res
reslove('成功p2');
});
var all = Promise.all([p1, p2]).then(function(data){
console.log(data);
}).catch(function (data) {
console.log(data);
});
}
execPromise();//["成功", "成功"]
上述代码:印证了第三点:当所有的子Promise都完成,该Promise完成,返回值是全部值的数组
function execPromise() {
var p1 = new Promise(function (reslove, reject) {
reslove('成功');
});
var p2 = new Promise(function (reslove, reject) {
reslove('成功');
});
var p3 = new Promise(function (reslove, reject) {
reject('失败');
});
var all = Promise.all([p1, p3, p2]).then(function (data) {
console.log(data);
}).catch(function (data) {
console.log(data);
});
}
execPromise();//失败
上述代码:印证了第四点:如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果.
Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示.
我们需要注意的是:
Promise.all获取的成功结果的数组顺序和Promise.all接收到的Promise对象数组顺序是一致的,也就是说p1在前面,即使是p1获取到的数据耗时比p2晚,也不会改变!这就带来了一个很大好处:在前端开发请求接口数据过程中,经常会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all,可以完美的解决这个问题
所以,Promise.all并不是并发执行的!
可以并行的执行多个请求,等请求全部完成之后在做处理,一般场景有:有多个数据接口我需要等所有的接口返回数据之后,才干后面的事
在Promise.all中,只要有一个请求失败,那么就会直接返回失败,后面的无法执行,显然这并不是我们想要的结果,也就是我们常说的
短路特性
既然有缺陷,我们就要弥补,可以通过给子Promise定义catch()的捕捉方法,然后返回resolve,那么Promise.all()的catch()就不会捕捉到~
function execPromise() {
var p1 = new Promise(function (reslove, reject) {
reslove('成功1');
});
var p2 = new Promise(function (reslove, reject) {
reslove('成功2');
});
var p3 = new Promise(function (reslove, reject) {
reject('失败');
});
Promise.all([p3, p1, p2].map(p => p.catch(function (err) {
console.log("我是报错",err);
}))).then(function (res) {
console.log("都执行了"+res);
}).catch(function (error) {
console.log(error);
});
}
execPromise();
//我是报错 失败
//都执行了,成功1,成功2
核心内容是map方法,其实就是给每个要执行的Promise对象加个catch()的处理,使用方式是通过map循环.map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。
Promise.race()
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
其实,顾名思义,Promise.race就是赛跑的意思,也就是说Promise.race([p1, p2, p3]);
里面那个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态,
原理比较简单,实际开发中,没有遇到什么类似场景,比较少用!
function execPromise() {
var p1 = new Promise(function (reslove, reject) {
reslove('成功1');
});
var p2 = new Promise(function (reslove, reject) {
reslove('成功2');
});
var p3 = new Promise(function (reslove, reject) {
reject('失败');
});
var all = Promise.race([p1, p3, p2]).then(function (data) {
console.log(data);
}, function (data) {
console.log(data);
});
}
execPromise(); //结果只会打印成功1
有时候我们不关系异步操作的结果,只关心这些操作有没有结束,这时,我们就可以考虑使用Promise.allSettled()了,如果没有这个方法,想要确保所有操作都结束.就很麻烦,Promise.all()无法做到这一点
Promise.allSettled()
方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束。该方法由 ES2020 引入。
与Promise.all不同的在于.其不会短路,也就是说当Promise全部处理完成之后我们可以拿到每个Promise的状态,而不管其是否处理成功.
function execPromise() {
var p1 = new Promise(function (reslove, reject) {
reslove('成功1');
});
var p2 = new Promise(function (reslove, reject) {
reslove('成功2');
});
var p3 = new Promise(function (reslove, reject) {
reject('失败');
});
Promise.allSettled([p3, p1, p2]).then(function(data){
console.log(data);
});
}
execPromise();