1.Promise的理解
1.1 基本概念
首先,Promise是一种异步编程的解决方案。通俗点讲,就是一个容器,里面存放着我们未来想要进行的事件(通常为一个异步操作)的结果。我们可以通过Promise以同步方法进行异步编程,避免出现多层嵌套回调。
1.2 Promise对象
Promise对象代表一个异步操作,其中包含三种状态:
- pending: 异步操作处于进行中
- fulfilled: 异步操作已完成
- rejected: 异步操作失败
此状态不受外界影响,只有异步操作的结果可以改变该状态。
2.基本用法
Promise是一个构造函数用于生成Promise实例,他所接受的参数是一个函数,该函数拥有resolved和rejected两个参数
下面代码生成了一个Promise实例:
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
异步操作成功时resoved函数使当前状态由pending(进行中) => fulfilled(已完成),而失败时rejected函数使当前状态由pending(进行中) => rejected(失败)。
2.1 .then()与.catch()方法
Promise实例生成以后,可以用then方法、catch方法分别指定Resolved状态和Rejected状态的回调函数:
const promise = new Promise(function(resolve,reject) {
setTimeout(() => { //这里使用 setTimeout 模拟异步操作
const a = 1;
// const a = 2;
if (a === 1) {
resolve('a等于'+ a)
} else {
reject('a不等于1,a为' + a)
}
},1000)
})
promise.then(value => {
console.log(value) // a等于1
}).catch((err) => {
console.log(err)
})
// 下方代码then的第二个参数可代替上方catch方法
// promise.then(value => {
// console.log(value) // a等于1
// }, err => {
// console.log(err) // a不等于1,a为2
// })
2.2 Promise的链式调用
Promise的优势就在于链式调用,现有多个异步任务需要顺序执行,如果不用Promise则会陷入一层一层的回调函数,也就是常说的“嵌套地狱”。
当我们下一个函数的参数是上一个函数的返回值时,我们的代码就可能出现以下情况: 假设业务开发中有3个接口需要异步执行
function first(param, fun) {
//延时模拟网络请求
setTimeout(function() {
console.log('方法一正在执行');
let firstResult = param + 1;
fun(firstResult);//参数依赖first方法的返回值
},1000)
}
function second(param, fun) {
setTimeout(function() {
console.log('方法二正在在执行')
let secondParam = param + 1;
console.log('我接受了第一个方法的返回值' + param)
fun(secondParam);
},1000)
}
function third(param) {
setTimeout(function() {
let finalParam = param + 1;
console.log('我是最终结果' + param)
},1000)
}
first(1,function(firstRes) {
second(firstRes, function(secondRes) {
third(secondRes)
})
})
这里我们可以看到,程序顺序输出结果:
可以看到,因为依赖关系,如果接口数量较多时,将会出现非常深的嵌套,这就形成了所谓的回调地狱。这种横向展开的代码使得代码的可读性变差。
而Promise就能简化这种层层回调的方法。实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,他比直接进行多次回调更见简单、灵活。因此,在遇到上述场景是,正确使用Promise的方法如下:
function first(param) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第一个方法正在执行')
let firstParam = param + 1;
resovled(firstParam)
},1000)
})
return promise;
}
function second(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('我是第二个方法')
console.log('我接受了第一个方法的返回值' + data)
let secondParam = data + 1;
resovled(secondParam)
},1000)
})
return promise;
}
function third(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('我是第三个方法')
console.log('我接受了第二个方法的返回值' + data)
resovled(data)
},1000)
})
return promise;
}
first(1).then((value1) => {
console.log(value1)
return second(value1)
})
.then((value2) => {
console.log(value2)
return third(value2)
})
.then((value3) => {
console.log(value3)
})
输出结果为:
从上面的结果我们可以看出,在first方法中传给resovled的值,能在接下来的.then方法中拿到。并且,在then方法中,也可以直接return一个Promise对象,这样在后面的.then中就可以接收到上一个方法中传给resolved的值。
从上述对比中我们可以清楚的看到,虽然Promise并没有对代码量有过多的简化,但是使得原来横向展开的嵌套地狱变为纵向展开,不管有多少个方法需要顺序执行,我们都可以通过 then的调用不停地串连起来使得代码更加清晰,易维护。
2.3 promise的其它方法
2.3.1 all()
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。从状态上说,只有当下面3个Promise 实例的状态都变成fulfilled,或者其中有一个变为rejected,才会调用Promise.all方法后面的回调函数。
function first(param) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第一个方法正在执行')
resovled(1)
},2000)
})
return promise;
}
function second(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第二个方法开始执行')
resovled(2)
},1000)
})
return promise;
}
function third(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第三个方法开始执行')
resovled(3)
},1000)
})
return promise;
}
Promise.all([first(),second(),third()])
.then(([a,b,c])=> {
console.log('3个方法都执行完毕')
console.log(a)
console.log(b)
console.log(c)
})
结果:
从结果中我们可以看到first方法执行时间较长,但其它执行较快的操作执行完后,回调函数并未立即执行,而是当3个异步操作的结果都返回后Promise.all方法后面的回调函数才会调用,同时输出 1,2,3。
2.3.1 race()
从字面意思来看,race是竞赛的意思。这样理解起来更加方便。race的作用与all恰恰相反,当只要任意一个异步操作执行完毕时会立即执行.then回调。
但这里需要注意一下,当第一个回调函数执行完毕后,其它的异步操纵并不会停止,而是继续进行。
function first(param) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第一个方法正在执行')
resovled(1)
},3000)
})
return promise;
}
function second(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第二个方法开始执行')
resovled(2)
},2000)
})
return promise;
}
function third(data) {
let promise = new Promise((resovled,rejected) => {
setTimeout(function() {
console.log('第三个方法开始执行')
resovled(3)
},1000)
})
return promise;
}
Promise.race([first(),second(),third()])
.then(function(results){
console.log('third先执行完毕')
})
结果如下:
可以看到当third最先执行完,执行完之后立即执行回调函数,同时,其它异步操作并未停止,而是继续执行输出结果。
总结
以上就是Promise的基本用法,这些方法也是实际项目中经常使用到的。除此之外还有done、finally、success、fail等方法,但这些并不在Promise标准中,是自己实现的语法糖。上述提到的进阶部分将在后续给出,真正掌握之后,相信大家对于Promise在其它的场景中的使用也会变得得心应手。