Promise
作为一个单词,有承诺、许诺的意思。那我说其实作为一个程序它也是这个意思。Promise
,它也是如此,在你创建之时,它就许下了一个承诺,在将来它会出现两个结果之一,要么失败要么成功(就像几天后我博客的访问量,不可能上了1k又没上1k)。Promise
的三种状态,分别是pendding
(承诺进行中),resolve
(成功,也称为Fulfilled
)和 Reject
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。真的有这么神奇吗?一个程序还能成精了不成!当然不是,下面我们就看看Promise
的执行过程
Promise
是一个构造函数,需要new
执行创建一个promise
实例,并必须接收一个函数参数then
方法的回调函数catch
方法的回调函数let p = new Promise(function(resolve, reject) {
// 回调函数里面是正在进行时
console.log("hello"); // hello
setTimeout(() => {
console.log("我是成功的延时器"); // 我是成功的延时器
resolve(); // 如果这里的延时器触发时间更短,执行第一个参数(resolve)的函数,第二个参数(reject)的函数就不会执行
}, Math.random() * 1000); // 延时器触发的时间随机
setTimeout(() => {
console.log("我是失败的延时器"); // 我是失败的延时器
reject(); // 如果这里的延时器触发时间更短,执行第二个参数(reject)的函数,第一个参数(resolve)的函数就不会执行了
}, Math.random() * 1000); // 延时器触发的时间随机
});
p.then(() => { // 实例的then方法在成功时执行
console.log("成功了");
});
p.catch(() => { // 实例的catch方法在失败时执行
console.log("失败了");
});
打印结果有两种:
在上述代码中,new Promise
的回调函数里称为pendding
,在这个状态下,所有的代码都会执行。如果第一个延时器触发的事件更短,就会执行resolve
的函数,即由实例的then
方法的回调函数传进来的代码,此时,reject
的函数就不会执行(结果只能存在一个);反之亦然
api
也可以写成以下语法then()
接收两个函数参数,第一个参数表示成功,第二个参数表示失败p.then(() => {
console.log("成功了");
},() => {
console.log("失败了");
});
Promise
的应用一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。
Promise
解决回调地狱let p1 = new Promise(function(resolve, reject) {
// 假设使用ajax请求数据,它的回调函数接收的参数就是请求到的数据
ajax(url1, function(res1) {
resolve(res1); // 请求成功,将数据传给外部的then方法
})
})
let p2 = p1.then(function(res1) {
// 在then方法的回调函数中在返回一个Promise的实例
return new Promise(function(resolve, reject) {
ajax(url2, function(res2) {
resolve(res2);
})
})
})
p2.then(function(res2) {
return new Promise(function(resolve, reject) {
ajax(url3, function(res3) {
resolve(res3);
})
})})
...
then
方法的回调函数内部返回一个新实例,我们就可以不断在外面拿到一个新实例,不断套娃…,这样就可以使代码变得优雅可维护Promise
的连缀写法p1.then(()=>{
return new Promise(()=>{}); // 整个then的方法都是p1的
}).then(()=>{
return new Promise(()=>{}); // 整个then的方法都是p2的
}).then(()=>{
return new Promise(()=>{}); // 整个then的方法都是p3的
})
Promise.all()
方法// 仍然假设ajax请求数据
let p1 = new Promise(function(resolve, reject) {
ajax(url1, function(res1) {
resolve(res1);
})
})
let p2 = p1.then(function(res1) {
return new Promise(function(resolve, reject) {
ajax(url2, function(res2) {
resolve(res2);
})
})
})
let p3 = p2.then(function(res2) {
return new Promise(function(resolve, reject) {
ajax(url3, function(res3) {
resolve(res3);
})
})
})
let p = Promise.all([p1,p2,p3]);
p.then((res)=>{ console.log(res); })
- 如果上述三个请求都能成功拿到数据,打印的最终结果会是三个数据
- 如果有一个请求失败了,最终一个数据都无法拿到
Promise.race()
方法// 仍然假设ajax请求数据
let p1 = new Promise(function(resolve, reject) {
ajax(url1, function(res1) {
resolve(res1);
})
})
let p2 = p1.then(function(res1) {
return new Promise(function(resolve, reject) {
ajax(url2, function(res2) {
resolve(res2);
})
})
})
let p3 = p2.then(function(res2) {
return new Promise(function(resolve, reject) {
ajax(url3, function(res3) {
resolve(res3);
})
})
})
Promise.race([p1, p2, p3]).then((res) => { // 使用了连缀写法
console.log(res); // 谁先执行完,就显示谁的数据
})
- 不管有没有失败,都只拿到第一个执行结束的结果(比如,某个请求的路径错误,但是这个请求最先执行完,最终的结果只有一个报错:如404之类的)
- 如果某个成功的请求最先执行完,最终的结果就是该请求拿到的数据
all
和race
区别(其实很语义化,理解起来不难)
race
:不管有没有失败,只拿到第一个执行完的数据all
:只要有失败就拿不到数据,只有全部成功,才能拿到所有数据