Promise 是ES6中的异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理(因为js是单线程的,那我们要解决线程阻塞的问题则必须采用异步操作)
promise对象有两个特点:
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise
对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
首先,我们来看一道面试经常遇到关于promise非常经典的问题,下面代码输出的顺序是什么:
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
很显然,答案就是:// 2,3,5,4,1,我们可以这么理解,延时器和promise同为异步操作,但延时器毫无疑问是最后执行的,然后第二慢执行的就是promise对象then方法中的异步操作,所以按顺序执行,可以得到,2,3,5,4,1的答案。
然后,我们来详细讲讲promise中的一些方法:
.then()方法:
简单来讲,then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作,这样比起原来回调处理异步操作则优雅的多,也便于维护。
我们从举例子来说明这些方法怎么用:先定义几个方法
//做饭
function cook(){
console.log('开始做饭。');
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('做完饭!');
resolve('辣椒炒肉');
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('吃完饭!');
resolve('一块碗和一双筷子');
}, 2000);
});
return p;
}
//洗碗
function wash(data){
console.log('开始洗碗:' + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('洗完碗!');
resolve('干净的碗筷');
}, 2000);
});
return p;
}
当我们需要按一定循序做这些事情的时候,我们则可以用.then()的链式语法来调用这些方法:
cook()
.then(function(data){
return eat(data);
})
.then(function(data){
return wash(data);
})
.then(function(data){
console.log(data);
});
我们可以得到如下结果: 开始做饭。
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:22 开始吃饭:辣椒炒肉
promise3.html?__hbt=1533796225113:25 吃完饭!
promise3.html?__hbt=1533796225113:33 开始洗碗:一块碗和一双筷子
promise3.html?__hbt=1533796225113:36 洗完碗!
promise3.html?__hbt=1533796225113:51 干净的碗筷
当然我们也可以用这样的简写方法调用,但会有点问题,后面我们会讲到:
cook()
.then(eat)
.then(wash)
.then(function(data){
console.log(data);
});
reject()方法:
上面样例我们通过 resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)。
//做饭
function cook(){
console.log('开始做饭。');
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('做完饭!');
reject('胡了的米饭');
}, 1000);
});
return p;
}
//吃饭
function eat(data){
console.log('开始吃饭:' + data);
var p = new Promise(function(resolve, reject){ //做一些异步操作
setTimeout(function(){
console.log('吃完饭!');
resolve('一块碗和一双筷子');
}, 2000);
});
return p;
}
然后我们调用并得到结果:
// cook()
// .then(eat,function(data){
// console.log(data+"没法吃")
// })
// 这两种调用方法是一样的
cook()
.catch(function(err){
console.log(err+"没法吃")
})
结果:
开始做饭。
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:59 胡了的米饭没法吃
如果我们只处理失败的结果,则可以使用:
.then(null,function(){})或者是使用后面的.catch()
.catch()方法:
cook()
.catch(function(err){
console.log(err+"没法吃")
})
当我们在.then()方法中任意位置抛出任意错误,我们都可以在.catch()中捕获并进行处理
all()方法:
Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调,两个个异步操作是并行执行的,等到它们都执行完后才会进到 then 里面。同时 all 会把所有异步操作的结果放进一个数组中传给 then。
我们同样拿上面吃饭和做饭的栗子:
Promise
.all([cook(),eat()])
.then(function(res){
console.log("a开始执行")
console.log(res)
})
结果:
开始做饭。
promise3.html?__hbt=1533796225113:22 开始吃饭:undefined
promise3.html?__hbt=1533796225113:14 做完饭!
promise3.html?__hbt=1533796225113:25 吃完饭!
promise3.html?__hbt=1533796225113:64 a开始执行
promise3.html?__hbt=1533796225113:65 (2) ["胡了的米饭", "一块碗和一双筷子"]
race()方法:
顾名思义,就是赛跑的意思,all()方法是灯所有方法执行在执行.then()里面的语句,而race()是只要有一个异步操作执行完成就会执行.then()里面的回调,但没有执行完的异步操作也会继续执行,而不是停止。
比如我们给一个请求设置超时时间:
//请求某个图片资源
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
我们把两个方法放在race()中赛跑,如果5秒内请求成功,则正常进入.then()方法中,如果5秒内图片还未请求回来,则报请求图片超时。
finally()方法:
不管promise
最后的状态,在执行完then
或catch
指定的回调函数以后,都会执行finally
方法指定的回调函数。不接受参数,和promise的执行结果无关
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
try()方法:
有时候不知道或者不想区分,函数f
是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f
是否包含异步操作,都用then
方法指定下一步流程,用catch
方法处理f
抛出的错误,但是如果是同步函数,放入.then()方法中,则会在本轮事件结尾执行,则try()方法就是让同步函数同步执行,异步函数异步执行。
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
到此我们就讲完了promise中的几种方法:注意区分Promise的方法和promise实例对象的方法:
let p = new Promise()
p.catch()
p.reject() // 没有这个东西
Promise.reject()
Promise.catch() // 没有这个东西
从而再次区分一下reject()和catch()两种方法:
第一:reject()是属于Promise对象的方法,Promise.reject(),而catch()是属于promise实例的方法,promise.prototype.catch()或者:var p = new Promise(), p.catch().then() ;
第二:reject()是抛出异常,而catch()是捕获异常并处理,比如说,我们reject("出异常了"),那我们catch()内就可以捕获到“出异常了”这个信息
最后一个,看过上面链式调用.then()方法的小伙伴不知道有没有发现一个问题:
方式一:
p1().then(p2).then(p3)
.then(function(data) {
console.log('data: ' + data);
})
.catch(function(error) {
console.log('error: ' + error);
});
function p1() {
return new Promise(function(resolve, reject) {
console.log('p1 resolved');
resolve(123);
});
}
function p2() {
return new Promise(function(resolve, reject) {
console.log('p2 rejected');
reject(456);
});
}
function p3() {
return new Promise(function(resolve, reject) {
console.log('p3 resolved');
resolve(789);
});
}
此种方式调用打印结果:p1 resolved , p2 rejected , error: 456,并没有打印出p3的结果,这是因为在一个promise链中,只要任何一个promise被reject,promise链就被破坏了,reject之后的promise都不会再执行,而是直接调用.catch方法,所以我们加入.catch()方法则可以捕获这些错误。
我们想要promise链不被破坏,则可以如下调用:(切记:只要返回的promise对象才可以调用promise中对应的方法)
var p1 = new Promise(function(resolve, reject) {
console.log("p1")
//when do something done
resolve();
});
var p2 = new Promise(function(resolve, reject) {
console.log("p2")
//when do something done
reject(123);
});
var p3 = new Promise(function(resolve, reject) {
console.log("p3")
//when do something done
resolve();
});
p1.then(function() {
return p2
})
.then(function() {
return p3
})
.catch(function(err){
console.log('错误为:',err)
})
打印的结果为:p1 , p2, p3 , 错误为: 123
到此promise中的一些知识点就讲完了,希望能帮助大家,有错误之处欢迎指正,谢谢