什么是Promise对象?
Promise是异步编程的一种解决方案,比起传统的解决方案(回调函数和事件),它显得更加的强大和方便(具体请看下文)。从语法上来讲,Promise是一个对象,从它可以获取异步操作的消息。Promise对象提供统一的API,各种异步操作都可以用同样的方法进行处理。
Promise有什么用?
大家一致会回答,解决回调地狱。那什么又是回调地狱?它有什么缺点?
如果要执行四个步骤,依次执行,那么传统我们可能会这么做:
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
复制代码
为了解决这种看起来不很舒服,写起来更不舒服的写法,我们可能会想起链式写法:
var obj = {
stepOne: function () {
console.log('one');
return this;
},
stepTwo: function () {
console.log('two');
return this;
},
stepThree: function () {
console.log('three');
return this;
}
}
obj.stepOne().stepThree().stepTwo();
复制代码
这种写法的核心在于,每次都返回this。 这样的好处在于:
- 每一个操作都是独立的一个函数
- 可以组装,也就是我们可以控制执行的流程
那如果我们还需要一些其他需求的时候呢?比如:
- 如果上一步的结果作为下一步的输入就更好了
- 如果出错我能够捕捉就更好了
- 如果我在函数中能够控制流程就好了 ....
这个时候,就需要用到我们强大的Promise了
基础用法
先来看看怎么使用吧!直接上代码:
const p1 = new Promise(function(resolve, reject) {
// 在这里做一些逻辑处理
// 如果异步操作成功的时候,调用resolve
if (true) {
resolve('success');
} else {
// 操作失败后我们就调用reject()
reject('fail');
}
})
p1.then((val) => {
console.log(val); // success
}, (err) => {
console.log(err)
})
复制代码
我们可以看到Promise是一个构造函数,用来生成Promise实例,它接收一个函数作为参数,这个函数有两个参数——resolve和reject(它们也是两个函数,已经由JavaScript引擎提供,不用自己部署)。
我们经常会在这个函数里处理一些逻辑,如果处理成功后我们会执行resolve(将状态从“未完成”转为“完成”),失败后执行reject(将状态从“未完成”转换为“失败”)。
三个状态
- pedding(未完成)
- resolved(完成)
- rejected(失败)
实例生成之后,我们可以使用then方法指定resolved和rejected状态的回调函数。像上面的代码,就相当于执行第一个回调函数,第二个不会执行(这个时候其实不提供第二个参数也是可以的)。
如果是执行失败,状态变成reject的时候,就会执行第二个回调函数,就会输出'fail'。但是其实我们不是很推荐这样的写法,我们可以将第二个回调函数写catch方法的形式。
const p1 = new Promise(function(resolve, reject) {
// 在这里做一些逻辑处理
// 如果异步操作成功的时候,调用resolve
if (false) {
resolve('success');
} else {
// 操作失败后我们就调用reject()
reject('fail');
}
})
p1.then((val) => {
console.log(val);
}).catch((err) => {
console.log(err); // fail
})
复制代码
这种写法类似于try...catch...,更加易于我们理解。这个时候我们其实就已经解决了出错我们能够捕捉的问题了。
控制代码的执行顺序
采用链式的then,可以指定一组按照次序调用的回调函数。这个时候前面的一个返回的可能是另外一个Promise对象(也就是说有异步操作)。这样后面的这个Promise就依赖于前面Promise对象的状态。
const p1 = new Promise(function(resolve, reject) {
console.log(1);
setTimeout(function() {
console.log(2);
resolve();
}, 1000);
})
const p2 = new Promise(function(resolve, reject) {
console.log(3);
resolve(p1);
})
p2.then(() => {
console.log(4);
}).then(() => {
console.log(5);
})
复制代码
以上代码,我们应该注意一点p2的resolve方法将p1作为参数,也就是说p2的执行依赖于p1的执行。当p2准备好的时候,p1可能还没准备好,这个时候p2就得等p1。
另外,需要注意的一点就是,then方法返回的是一个新的Promise实例(注意,不是之前的Promise实例),因此可以采用链式写法,即then方法之后再调用另一个then方法。这样,我们就可以实现了上面的需求了!
Promise的几个重要方法
下面简单介绍一下Promise对象的几个方法,我们经常也会用到。
Promise.all()
直接上代码:
const p = Promise.all([p1, p2, p3]);
复制代码
Promise.all方法接收一个数组作为参数,p1、p2、p3都是Promise实例。这个时候p的状态由p1、p2、p3。它们的依赖机制类似于电路中的串联,现在我们使用三条线(就是p1、p2、p3)进行串联,p就是最后灯泡,灯泡要亮的话,三个都要成功,只要其中的一个没有成功,那么p的状态就是rejected。
Promise.race()
const p = Promise.race([p1, p2, p3]);
复制代码
这个方法也是接受一个数组作为参数。race的英文意思就有竞赛的意思。上面的代码中表示,p1、p2、p3中哪个胜出(最新状态发生改变,不管是失败还是成功),那么p的状态就会跟着它改变。
Promise.resolve()
作用:将现有对象转换成Promise对象,这样我们就可以调用then方法等
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
复制代码
Promise.reject()
Promise.reject(reason)方法也会返回一个Promise实例,该实例状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
复制代码
参考: https://cnodejs.org/topic/560dbc826a1ed28204a1e7de
http://es6.ruanyifeng.com/#docs/promise