Promise介绍

Promise是异步编程的一种解决方案(ES7开始正式上线),用于处理回调地狱的问题。

下面是一个典型的回调地狱,

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('得到最终结果: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

我们把他改成promise试试

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('得到最终结果: ' + finalResult);
})
.catch(failureCallback);

代码可读性非常强

Promise对象的创建

下面我们列举一个Promise的基本形态

const p=new Promise((resolve,reject)=>{
    
})

promise接收一个函数,这个函数的两个参数resolvereject,其作用是让函数返回结果正确还是错误。

现在我们创建一个Promise对象

//p是一个promise对象
const p=new Promise((resolve,reject)=>{
    //进行处理
    let data=Math.random()*5
    if(data>2){
        resolve('成功')
    }else{
        reject('失败')
    }
})

创建好了又要怎么使用呢?

Promise对象的使用

promise对象有3个常用的方法

  • then()
  • catch()
  • finally()

then()

then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数

如果上一个promise返回的是resolve(成功)的结果,那么就回触发resolved的函数,或者触发reject(失败)的函数。

p.then((resolve)=>{
    console.log(resolve) //打印:成功
},(reject)=>{
    console.log(reject) //打印:失败
})

then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因,下面举个例子

p1.then((req)=>{
    console.log(req)
    return p2
})
.then(req => {
    console.log(req)
})

catch()

catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止.

p.then(req=>{
    console.log(req);
}).catch(e=>{
    console.log('catch',e);
})
//输出 catch 失败

如果错误在之前被捕获,则不会进入catch

p.then(req=>{
    console.log(req);
},res =>{
    console.log(res);
}).catch(e=>{
    console.log('catch',e);
})
//输出 失败

大部分情况下,catch用于代替then的第二个参数。

此时补充一点,promise内部抛出的错误不会传递到外部,即不会退出进程

try{
    p.then(req=>{
        console.log(req);
        console.log( NoDefinedValue +1)
    }).catch(e=>{
        console.log('catch捕获',e);
    })
}catch(e){
    console.error('外层捕获',e)
}

/*
控制台输出:
catch捕获 ReferenceError: NoDefinedValue is not defined
*/

finally()

无论请求是否成功,finally都会执行

p.then(req=>{
    console.log(req);
}).catch(e=>{
    console.log('catch捕获',e);
}).finally(()=>{
    //没有参数传递
    console.log('finally')
})

Promise的构造函数方法

all()

Promise.all()方法用于将多个 Promise实例,包装成一个新的 Promise实例

const p = Promise.all([p1, p2, p3]);

接受一个数组(迭代对象)作为参数,数组成员都应为Promise实例

实例p的状态由p1p2p3决定,分为两种:

  • 只有3个实例全部成功,p的状态才为成功,并返回成功的数组
  • 只要3个使用有一个失败,p的状态就为失败,并且将第一个失败的值返回

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()catch方法

成功案例:

let s1=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s1');
    },1000);
});
let s2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s2')
    },1000);
});
let s3=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s3');
    },1000);
});
let s=await Promise.all([s1,s2,s3]).then(result => {
    console.log('s成功',result)
},(res)=>{
    console.log('s失败',res)
})
//打印 s成功 ['s1', 's2', 's3']

失败案例:

let s1=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s1');
    },1000);
});
let s2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s2')
    },1500);
});
let s3=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s3');
    },1000);
});
let s=await Promise.all([s1,s2,s3]).then(result => {
    console.log('s成功',result)
},(res)=>{
    console.log('s失败',res)
})
//打印 s失败 s3

如果p2没有catch,那么会被Promise.all().catch()捕获,否则则在p2中处理

race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例

const p = Promise.race([p1, p2, p3]);

只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变

率先改变的 Promise 实例的返回值则传递给p的回调函数

let s1=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s1');
    },1000);
});
let s2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s2')
    },1500);
});
let s3=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s3');
    },1100);
});
let s=await Promise.all([s1,s2,s3]).then(result => {
    console.log('s成功',result)
},(res)=>{
    console.log('s失败',res)
})
//打印 s成功 s1

allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例

只有等到所有这些参数实例都返回结果,不管是成功还是失败,包装实例才会结束

let s1=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve('s1');
    },1000);
});
let s2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s2')
    },1500);
});
let s3=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        reject('s3');
    },1100);
});
let s=await Promise.all([s1,s2,s3]).then(result => {
    //无论s1,s2,s3结果如何,都会来到这里
    console.log('s成功',result)
})

resolve

将现有对象转为 Promise对象

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

参数可以分成四种情况,分别如下:

  • 参数是一个 Promise 实例,promise.resolve将不做任何修改、原封不动地返回这个实例
  • 参数是一个thenable对象,promise.resolve会将这个对象转为 Promise对象,然后就立即执行thenable对象的then()方法
  • 参数不是具有then()方法的对象,或根本就不是对象,Promise.resolve()会返回一个新的 Promise 对象,状态为resolved
  • 没有参数时,直接返回一个resolved状态的 Promise 对象

reject

该方法与resolve类似,只是状态为reject,不做展示。

Q.E.D.