Promise

158 阅读4分钟

Promise 是ES6异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调,它比传统的解决方案回调函数和事件更合理和更强大。

为什么要使用promise

最重要也是最主要的一个场景就是ajax和axios请求。通俗来说,由于网速的不同,可能你得到返回值的时间也是不同的,但是我们下一步要执行的代码依赖于上一次请求返回值,这个时候我们就需要等待,结果出来了之后才知道怎么样继续下去。

作用:

1.可以避免多层异步调用嵌套问题(回调地狱)
2.Promise 对象提供了简洁的API,使得控制异步操作更加容易(js执行机制导致的异步问题)

promise的好处

Promise对象有三种状态,他们分别是:

1.pending: 等待中,或者进行中,表示还没有得到结果

2.resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行

3.rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆

用法

创建Promise实例

**

let promise = new Promise(function(resolve, reject){
    // 你自己的异步逻辑代码
    
    if (/* 异步操作成功 */) {
        resolve(value);
    } else { // 异步操作失败
        reject(error);
    }
})

  Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由JavaScript引擎提供,不用自己部署。

  resolve作用是将Promise对象状态由“未完成”变为“成功”,也就是Pending -> Fulfilled,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
reject函数则是将Promise对象状态由“未完成”变为“失败”,也就是Pending -> Rejected,在异步操作失败时调用,并将异步操作的结果作为参数传递出去。

then

  Promise实例生成后,可用then方法指定一种状态回调参数:

1.Promise对象状态改为Resolved时调用 (必选)

备注:通俗的讲 resolve => then

catch

  Promise实例生成后,可用catch方法指定一种状态回调参数:
1.Promise对象状态改为Rejected时调用 (可选)

备注:通俗的讲 reject => catch

基本用法示例

**

function sleep(num) {
    return new Promise(function(resolve, reject) {
       if (num>10){
          resolve('成功')                      
       } else {
          reject('失败')     
       }  
    })
}
sleep(500).then( res=> console.log(res)).catch(err => console.log(err));

  这段代码定义了一个函数sleep,调用后,如果传入的实参大于10,你会发现他会触发then的回调函数,并且打印出成功,如果传入的实参小于10,你会发现他会触发catch的回调函数,并且打印出失败。

执行顺序

  接下来我们探究一下它的执行顺序,看以下代码:

**

let promise = new Promise(function(resolve, reject){
    console.log("AAA");
    resolve()
});
promise.then(() => console.log("BBB"));
console.log("CCC")

// AAA
// CCC
// BBB

执行后,我们发现输出顺序总是
AAA -> CCC -> BBB。表明,在Promise新建后会立即执行,所以
首先输出 AAA。然后,then方法指定的回调函数将在当前脚本所有同步任务执行完后才会执行,所以BBB 最后输出。

与定时器混用

  首先看一个下面的代码:

**

let promise = new Promise(function(resolve, reject){
    console.log("1");
    resolve();
});
setTimeout(()=>console.log("2"), 0);
promise.then(() => console.log("3"));
console.log("4");

可以看到,结果输出顺序总是:
1 -> 4 -> 3 -> 2 ;1与4的顺序不必再说,而2与3先输出Promise的then,而后输出定时器任务。原因则是Promise属于JavaScript引擎内部任务,而setTimeout则是浏览器API,而引擎内部任务优先级高于浏览器API任务,所以有此结果。

async和await

async和await是解决异步的终极方案,与promise不排斥

image.png

 async function timeout() {
     return 'hello world!'
 }

await

await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。

await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;

async function fun(){
    let a = await 1;
    let b = await new Promise((resolve,reject)=>{
        setTimeout(function(){
            resolve('setTimeout')
        },3000)
    })
    let c = await function(){
        return 'function'
    }()
    console.log(a,b,c)
}
fun(); // 3秒后输出: 1 "setTimeout" "function"

function log(time){
    setTimeout(function(){
        console.log(time);
        return 1;
    },time)
}
async function fun(){
    let a = await log(1000);
    let b = await log(3000);
    let c = log(2000);
    console.log(a);
    console.log(1)
}
fun(); 
// 立即输出 undefined 1
// 1秒后输出 1000
// 2秒后输出 2000
// 3秒后输出 3000

async与await的使用

// 使用async/await获取成功的结果

// 定义一个异步函数,3秒后才能获取到值(类似操作数据库)
function getSomeThing(){
    return new Promise((resolve,reject)=>{
        setTimeout(()=>{
            resolve('获取成功')
        },3000)
    })
}

async function test(){
    let a = await getSomeThing();
    console.log(a)
}
test(); // 3秒后输出:获取成功

async和await的优化,多个await

 
    // 写法一
    let [cache, cache2] = await Promise.all([this.cachePromise,  this.cachePromise2]);
    // 写法二
    let p= this.cachePromise();
    let p2= this.cachePromise2();
    let cache= await p;
    let cache2= await p2;