ES6 Promise补充

Promise介绍

Promise是异步编程的一种解决方案,从语法上来看Promise是一个对象,从它可以获取异步操作的消息,简单来说,它就像是一个容器,里边保存着某个未来结束的事件,通常是获取异步操作的结果。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),一旦状态改变就不会在改变,任何时候得到的都是这个结果。Promise状态改变无非两种结果,一种是从pending到fulfilled,一种是从pending到rejected

finally

finally方法用于指定不管Promise对象最后的状态如何,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数

promise.then(value => {
    状态为fulfilled 执行
}).catch(error => {
    状态为rejected 执行
}) .finally(() => {
    不管状态为什么都会执行
});

finally不接收任何参数,这就意味着没办法知道Promise返回的状态如何,那么,finally方法里边的操作与状态无关,不依赖Promise的执行结果

all

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

//三个状态都成功
    const promise1 = new Promise((resolve,reject)=>{
        resolve('success1')
    })
    const promise2 = new Promise((resolve,reject)=>{
        resolve('success2')
    })
    const promise3 = new Promise((resolve,reject)=>{
        resolve('success3')
    })
    
    const p = Promise.all([promise1,promise2,promise3])
    
    p.then(data=>{ 
        console.log(data); //三个都成功则成功 
    }, error=>{ 
        console.log(error) // 只要有失败,则失败 
    })

在这里插入图片描述
这是上边代码执行出来的结果,现在让我看看有一个失败的会怎样

//三个状态都成功
    const promise1 = new Promise((resolve,reject)=>{
        resolve('success1')
    })
    const promise2 = new Promise((resolve,reject)=>{
        reject('error')
    })
    const promise3 = new Promise((resolve,reject)=>{
        reject('error1')
    })
    
    const p = Promise.all([promise1,promise2,promise3])
    
    p.then(data=>{ 
        console.log(data); //三个都成功则成功 
    }, error=>{ 
        console.log(error) // 只要有失败,则失败 
    })

在这里插入图片描述
通过上面的代码可以看出,p的状态是由promise1 promise2 promise3决定的,分为两种情况:

  1. 只有promise1 promise2
    promise3的状态都为fulfilled的时候,那么p的状态也为fulfilled,此时promise1 promise2
    promise3的返回值组成一个数组,传给p的回调函数
  2. 只要 promise1 promise2 promise3 之中有一个被 rejected,p的状态就变成
    rejected,此时第一个被reject 的实例的返回值,会传递给p的回调函数。

race

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

  const promise1 = new Promise((resolve,reject)=>{
       setTimeout(() => { 
           resolve('成功'); 
       }, 2000);
    })
    const promise2 = new Promise((resolve,reject)=>{
         setTimeout(() => { 
           reject('失败'); 
       }, 5000);
    })
    
    const p = Promise.race([promise1,promise2])
    
    p.then(data=>{ 
        console.log(data); 
    }, error=>{ 
        console.log(error)
    })


在这里插入图片描述
从上边代码的结果可以看出,只要promise1 promise2 之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

resolve

有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。

resolve方法的参数分为四种情况:

  1. 参数是一个Promise实例的

resolve方法不做任何的处理,原封不动返回

  1. 参数是一个具有then方法的对象
    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };

    let p1 = Promise.resolve(thenable);
    p1.then(function (value) {
      console.log(‘H:’,value);  // H:42
    });

resolve方法会将这个对象转为Promise对象,然后立即执行对象里边的then方法

  1. 参数不是具有then()方法的对象,或根本就不是对象
   const p = Promise.resolve('Hello');

   p.then(function (s) {
     console.log(s)
   });
   // Hello

如果参数是一个原始值,或者是一个不具有then方法的对象,则resolve方法返回一个新的 Promise 对象,状态为resolved。

  1. 不带参数的
const p = Promise.resolve();

p.then(function () {
  // ...
});

resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用resolve方法。

reject

reject方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log('H:',s)
});
// H: 出错了

上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

reject方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。

async await

async/await是JavaScript为了更好的解决异步问题而提出的一种解决方案,许多人将其称为异步的终极解决方案,下面让我们一起来看一下async/await

function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms);
  console.log(value);
}

asyncPrint('hello world', 2000);

在这里插入图片描述
在这里插入图片描述

使用注意点

  1. await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。
async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

  1. 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
  let foo = await getFoo();
  let bar = await getBar();

上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。

可以写成这样:

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

  1. await命令只能用在async函数之中,如果用在普通函数,就会报错
async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}

在这里插入图片描述

优雅的错误处理方法

上边我们也说到async返回一个Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中,如果是有多个异步操作,需要对每个异步返回的error错误进行不同的处理,我们可以这样写:

const fetchDataA = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('fetch data is A')
        }, 1000)
    })
}

const fetchDataB = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('fetch data is B')
        }, 1000)
    })
}

const fetchDataC = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('fetch data is C')
        }, 1000)
    })
}

(async () => {
    try {
        const dataA = await fetchDataA()
        console.log('dataA is ->', dataA)
    } catch(err) {
        console.log('err is ->', err)
    }

    try {
        const dataB = await fetchDataB()
        console.log('dataB is ->', dataB)
    } catch(err) {
        console.log('err is ->', err)
    }

    try {
        const dataC = await fetchDataC()
        console.log('dataC is ->', dataC)
    } catch(err) {
        console.log('err is ->', err)
    }
})()

这样处理也不是不可以,但是代码里乍一看全是try…catch,看着不是那么优雅,这时我们是不是也想只用一个try…catch

// ... 这里 fetch 函数省略

(async () => {
    try {
        const dataA = await fetchDataA()
        console.log('dataA is ->', dataA)
        const dataB = await fetchDataB()
        console.log('dataB is ->', dataB)
        const dataC = await fetchDataC()
        console.log('dataC is ->', dataC)
    } catch(err) {
        console.log('err is ->', err)
        // 难道要定义 err 类型,然后判断吗??
        /**
         * if (err.type === 'dataA') {
         *  console.log('dataA err is', err)
         * }
         * ......
         * */
    }
})()

如果是这样的话那我们岂不是要写一对if else的判断,这时候我们在想async返回的是Promise对象,那么我们就可以用then方法

(async () => {
    const fetchData = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('fetch data is me')
            }, 1000)
        })
    }

    const data = await fetchData().then(data => data ).catch(err => err)
    console.log(data)
})()

在上面写法中,如果 fetchData 返回 resolve 正确结果时,data 是我们要的结果,如果是 reject 了,发生错误了,那么 data 是错误结果,这样我们就又要判断了,显然不太合适。

(async () => {
    const fetchData = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('fetch data is me')
            }, 1000)
        })
    }

    const [err, data] = await fetchData().then(data => [null, data] ).catch(err => [err, null])
    console.log('err', err)
    console.log('data', data)
    // err null
    // data fetch data is me
})()

这样是不是好很多了呢,但是问题又来了,不能每个 await 都写这么长,写着也不方便也不优雅,并且会有很多的重复代码

(async () => {
    const fetchData = () => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('fetch data is me')
            }, 1000)
        })
    }

    // 抽离成公共方法
    const awaitWrap = (promise) => {
        return promise
            .then(data => [null, data])
            .catch(err => [err, null])
    }

    const [err, data] = await awaitWrap(fetchData())
    console.log('err', err)
    console.log('data', data)
    // err null
    // data fetch data is me
})()

将对 await 处理的方法抽离成公共的方法,在使用 await 调用awaitWrap 这样看起来是不是就好多了

结语

上面就是我关于Promise和async/await理解的记录,
同时我也希望大家通过观看这篇文章能够有所收获!谢谢您的观看!
参考文献:
https://es6.ruanyifeng.com/
https://juejin.cn/post/6844903767129718791

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值