Promise 基本用法
promise 解决了什么问题
如果别人问我 Promise 解决了什么问题?我会毫无疑问的回答解决了回调地狱的问题。所以要想弄清楚 Promise 到底是个什么玩意儿,还要弄清楚以下几个问题:
- 什么是回调地狱
- 为什么会有回调地狱
- promise 是如何解决回调地狱问题的
在实际的开发过程中,我们通常会遇到一种情况:当某个值达到一种状态的时候,立刻去执行另外一个任务,这时候就用到了回调函数,如下
var dynamicFunc = function(cb) {
setTimeout(function() {
cb(1,2);
}, 1000);
}
dynamicFunc(function(a,b,cb) {
const sum = a + b
cb(sum)
});
👆上面的代码展示了,当我调用 dynamicFunc 函数的时候,想要一秒后计算一下 1 + 2 的值 ,这时候就用到了回调函数,在回调函数里面,我想要值计算完用到这个值,去执行另外的一个逻辑,就会继续用到回调函数。所以 当函数里面值状态改变需要执行另外的逻辑就要使用回调函数。 这样我门的代码就会横向变宽,形成了回调地狱。
由于 JavaScript 是一门单线程的语言,所以在promise 出现之前,我门解决异步的场景就需要用到回调函数,如下
$.ajax({
url:'...',
...
success:function(response){
....
}
})
👆上面的代码示例是一个ajax 请求,发送请求后,⼀段时间服务端响应之后我们才能拿到结果。如果我们希望在异步结束之后执⾏某个操作,就只能通过回调函数这样的⽅式进⾏操作。 这就是一个典型的回调地狱。
在 ES6 中 js 产⽣了⼀个名为 promise 的新规范,使用另外的一种方式解决了回调地狱问题。ajax 请求会有两个 success 和 error。在 promise 里面也有两个函数 resolve 和 reject 分别表示已完成 和 已拒绝,表达的状态和 ajax 类似但是调用方式从 横向 转为 **纵向;**如下
const promise = () =>{
return new Promise((resolve,reject)=>{
$.ajax({
url:'...',
...
success:function(response){
resolve(response)
}
})
})
}
promise().then(response=>{console.log(response)}) // Ajax 请求结果
promise 介绍
Promise 是 ES6 新增的一个引用类型,可以通过 new 操作符来实例化,在创建promise 实例的时候需要传入一个函数作为参数。基础语法如下
const p = new Promise(()=>{});
console.log(p) // Promise <pending>
在控制台输出 promise 实例,会输出一个处于 **待定(pending) **状态的 promise。根据输出可以看出 promise 是一个有状态的对象,promise 的状态只能处于以下三种的一种:
- pending 待定
- fulfilled 解决
- rejected 拒绝
pending 是 promise 的最初状态,当处于 pending 状态的时候,根据相关逻辑状态可以改变(书上称作为落定settled)为 fulfilled 或者 rejected 状态。但是不管 promise 处于那种状态,都是不可逆的,并且如果promise 处于 fulfilled 或者 rejected 状态都是不可改变的。
实例化 promise 传入的函数通常称作 **执行器(executor) **函数。函数里面有两个函数作为参数 resolve 和 **reject。**通过调用这两个函数来改变 promise 的状态 resolve 会把状态改变为 fulfilled。reject 会把状态改变为 rejected。
const pPending = new Promise(()=>{});
console.log(pPending) // Promise <pending>
const pFulfilled = new Promise((resolve,reject)=>resolve());
console.log(pFulfilled) // Promise <fulfilled>
const pRejected = new Promise((resolve,reject)=>reject());
console.log(pRejected) // Promise <rejected>
Promise 实例创建完成后,可以通过 then 方法获得到 fulfilled 和 rejected 状态。基本语法如下
const promise = (val) =>{
return new Promise((resolve,reject)=>{
if(val){
resolve(val)
}else{
reject(val)
}
})
}
promise(true)
.then(()=>{
// fulfilled
},()=>{
// rejected
})
可以看出 then 方法也接受两个函数,分别对应resolve 和 reject 的处理结果。第二个函数可选,如果不写第二个函数可以通过 catch 方法 处理 reject 结果,如下
promise(true)
.then(()=>{
// fulfilled
})
.catch(()=>{
// rejected
})
promise 常用方法
- Promise.resolve()
会返回一个 Promise 实例,并且把状态改为 fulfilled。如下
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
在使用这种方式的时候,传参传值分为四种情况
- Promise 实例
如果参数是一个 promise 实例,那么会把这个实例返回,可以说和没使用这个方法是一样的哦。
- 含有then方法的对象
如果参数是一个具有 then 方法的对象,如果then方法是一个 thenable 函数则调用 promise.resolve() 方法会返回 对象里面then方法返回值;如果then方法是一个普通函数,不会有任何响应。如下
const thenObj = {
then:()=>{
return "Lius"
}
}
const p1 = Promise.resolve(thenObj);
p1.then(res=>{console.log(">>>>",res)}); // 不输出
const thenable = {
then:(resolve,reject)=>{
resolve("Lius")
}
}
const p2 = Promise.resolve(thenable);
p2.then(res=>{console.log(res)}); // Lius
- 不含有 then 方法的对象,或者不是一个对象
如果传入的是一个普通对象,或者不是一个对象,会把原值返回;
const obj = {
name:"Lius"
}
const p1 = Promise.resolve(obj);
const p2 = Promise.resolve("Lius");
p1.then(res=>{console.log(res)}); // {name: "Lius"}
p2.then(res=>{console.log(res)}); // Lius
- 不传参
如果不传参数,会返回一个状态 fulfilled 的 promise实例;
const p1 = Promise.resolve();
console.log(p1); // Promise <fulfilled>
- Promise.reject()
返回一个状态为 rejected 的 promise 实例,调用 Promise.reject()方法,会把传参全部当做reject的原因返回,可以在catch方法里面捕捉
- promise.all()
将多个promise 实例,封装成一个promise实例,基本语法如下
const p = Promise.all([p1,p2,p3])
Promise.all() 方法接收一个数组作为参数,需要注意的是数组元素都需要是promise实例,如果元素不是promise实例,会先调用promise.resolve方法转化为一个promise实例在进行处理。
此时 p 的状态,由 p1,p2,p3 同时决定,(1) 如果 p1, p2, p3 三个状态都为 fulfilled 那么 p 的状态就是 fulfilled;(2) 如果 p1, p2, p3 其中有一个状态是 rejected 那么 p 的状态就是 rejected。
在then 方法会按照传入参数的顺序,返回一个同样结果顺序的数组
const p1 = Promise.resolve("Lius")
const p2 = Promise.resolve("Lius1")
const p3 = Promise.resolve("Lius2")
Promise.all([p1,p2,p3])
.then(res=>{
const [p1Res,p2Res,p3Res] = res;
console.log(p1Res,p2Res,p3Res); // Lius, Lius1, Lius2
})
- promise.race()
Promise.race() 和 Promise.all() 的区别就在于 状态的改变,
p 的状态改变,只会受到p1, p2, p3, 其中的一个状态的影响,至于受到谁的影响,就要看他们谁先返回状态,率先返回的状态就是 p 的状态。
const p1 = Promise.resolve("Lius");
const p2 = () => {
return new Promise((resolve,reject)=>{
setTimeout(reject("ERROR"),5000)
})
}
const p = Promise.race([p1,p2])
p.then(res=>{
console.log("SUCCESS",res) // SUCCESS, Lius
})
.catch(err=>{
console.log("ERR.", err)
})
如何链式调用
需要注意的是 Promise.all() 和 Promise.race() 都是异步调用,在特殊的业务场景,我们需要链式调用,把 p1 返回的结果,作为参数传入 p1。常用的方法有两种,直接贴代码把
function promiseCreator(val, time) {
return new Promise(resolve => {
setTimeout(resolve(val), time);
})
}
function promiseCreator2(val, time) {
return new Promise(resolve => {
setTimeout(resolve(val), time);
})
}
const promiseCreatorList = [
promiseCreator,
promiseCreator2,
];
// 第一种 使用 reduce
const promiseChain = promiseList =>
promiseList
.reduce((acc, cur, index) =>
acc
.then(x => cur(`这是第${index}个promis;上一个值为=>${x}`, (index + 1) * 1000)),
Promise.resolve(0)
)
promiseChain(promiseCreatorList).then(res => console.log(res))
// 第二种,使用 for of
const promiseChain2 = promiseList => {
let result = Promise.resolve(0)
for (const [index, promise] of promiseList.entries()) {
result = result.then(x => promise(`这是第${index}个promis;上一个值为=>${x}`, (index + 1) * 1000))
}
return result
}
promiseChain2(promiseCreatorList).then(res => console.log(res))
本文解释了 promise 解决了什么问题,promise 的如何使用,和在实际开发过程中常用的promise静态方法。下一篇将要介绍 promise 规范和自己实现一个 promise