← 返回首页
Promise与async/await的区别(二)
发表时间:2022-08-22 16:59:22
Promise与async/await的区别(二)

async/await是基于promises的语法糖。可以说是对 Promise的有益补充。

1.async/await

快速掌握async/await的用法,你只需要理解以下几点:

  1. async/await是ES7新特性,比Promise出现的晚些。
  2. async/await是基于Promise实现的,它不能用于普通的回调函数。
  3. async/await最大的优点是解决了回调地狱问题,使得异步代码看起来像同步代码。
  4. await 右侧的表达式一般为promise,且只在async函数内有效。通常async/await都是成对出现的。

什么是地狱回调呢?

接着我们上一节的Promise案例,这里就会产生经典的回调地狱。

function eat() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('吃饭...')
            resolve('success')
        }, 5000);
    })
}

function drink() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('喝茶...')
            resolve('success')
        }, 1000);
    })
}

function bath(){
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            console.log('洗澡...')
            reslove('success')
        }, 3000);
    })
}


let cooking = (flag) => {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            if (flag) {
                reslove('success')
            }
            reject('fail')
        }, 5000);
        console.log('做饭无聊时,拍个抖音.....')
    })
}

console.log('准备开始做饭了....')
let p = cooking(true);
p.then(resp => {
    console.log(resp)
    if (resp === 'success') {

        eat().then(resp => {
            if(resp === 'success'){
                drink().then(resp=>{
                    if(resp === 'success'){
                        bath().then(resp=>{

                        })
                    }
                })
            }
        })
    }
}).catch(err => {
    console.log(err)
})

console.log('做饭的同时干些其它事情....')

运行结果:

准备开始做饭了....
做饭无聊时,拍个抖音.....
做饭的同时干些其它事情....
success 
吃饭... 
喝茶... 
洗澡... 

这种多层回调函数的相互嵌套,就形成了回调地狱现象。async/await的出现就完美的解决了回调地狱问题。

以下有小伙伴的实现方式:

let cooking = (flag) => {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            if (flag) {
                reslove('success')
            }
            reject('fail')
        }, 5000);
        console.log('做饭无聊时,拍个抖音.....')
    })
}

async function eat() {
    setTimeout(() => {
        console.log('吃饭...')
    }, 5000);
    return 'success';
}

async function drink() {
    setTimeout(() => {
        console.log('喝茶...')
    }, 2000)
    return 'success';
}

async function bath() {
    setTimeout(() => {
        console.log('洗澡...')
    }, 3000)
    return 'success'
}

console.log('准备开始做饭了....')
async function haveGoodTime() {
    await cooking(true);
    await eat();
    await drink();
    await bath();
}
console.log('做饭的同时干些其它事情....')

haveGoodTime();

运行结果:

准备开始做饭了....
做饭的同时干些其它事情.... 
做饭无聊时,拍个抖音.....  
喝茶... 
洗澡... 
吃饭...

我们发现又出现了先喝茶,洗澡,再吃饭的尴尬次序。错误的原因在于await后面并不是Promise对象。因此无法保证线程同步。

let cooking = (flag) => {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            if (flag) {
                reslove('success')
            }
            reject('fail')
        }, 5000);
        console.log('做饭无聊时,拍个抖音.....')
    })
}

async function eat() {
    return new Promise((reslove,reject)=>{
        setTimeout(() => {
            console.log('吃饭...')
            reslove('success')
        }, 5000);
    })
}

async function drink() {
    return new Promise((reslove,reject)=>{
        setTimeout(() => {
            console.log('喝茶...')
            reslove('success')
        }, 2000);
    })
}

async function bath(time) {
    return new Promise((reslove,reject)=>{
        setTimeout(() => {
            console.log('洗澡...')
            reslove('success')
        }, 3000);
    })
}

console.log('准备开始做饭了....')
async function haveGoodTime() {
    await cooking(true);
    await eat();
    await drink();
    await bath();
}
console.log('做饭的同时干些其它事情....')

haveGoodTime();

运行结果:

准备开始做饭了....         
做饭的同时干些其它事情.... 
做饭无聊时,拍个抖音.....  
吃饭... 
喝茶... 
洗澡... 

执行次序完全正确,而且使得异步代码的执行看起来像同步代码的写法。

我们进一步优化,把每次生成Promise的过程封装成一个方法,大大提高了代码的复用性。

async function takeLongTime(value,time) {
    return new Promise((resolve,reject) => {
        console.log(value);
        setTimeout(() => resolve('success'), time);
    });
}

let cooking = (flag) => {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            if (flag) {
                reslove('success')
            }
            reject('fail')
        }, 5000);
        console.log('做饭无聊时,拍个抖音.....')
    })
}

async function eat(time) {
    return takeLongTime('吃饭...',5000);
}

async function drink(time) {
    return takeLongTime('喝茶...',2000);
}

async function bath(time) {
    return takeLongTime('洗澡...',3000);
}


console.log('准备开始做饭了....')
async function haveGoodTime() {
    await cooking(true);
    await eat();
    await drink();
    await bath();
}
console.log('做饭的同时干些其它事情....')

haveGoodTime();

执行次序完全相同。同理,如果在吃饭时发生意外,那么也无法继续喝茶和洗澡。但是要记得捕获异常呦!

async function takeLongTime(value,time) {
    return new Promise((resolve,reject) => {
        console.log(value);
        if(value === '吃饭...') {
            reject('吃饭噎住了...')
        }
        setTimeout(() => resolve('success'), time);
    });
}

let cooking = (flag) => {
    return new Promise((reslove, reject) => {
        setTimeout(() => {
            if (flag) {
                reslove('success')
            }
            reject('fail')
        }, 5000);
        console.log('做饭无聊时,拍个抖音.....')
    })
}

async function eat(time) {
    return takeLongTime('吃饭...',5000);
}

async function drink(time) {
    return takeLongTime('喝茶...',2000);
}

async function bath(time) {
    return takeLongTime('洗澡...',3000);
}


console.log('准备开始做饭了....')
async function haveGoodTime() {
    try {
        await cooking(true);
        await eat();
        await drink();
        await bath();
    }catch(err){
        console.log(err)
    }
}
console.log('做饭的同时干些其它事情....')

haveGoodTime();

运行结果:

准备开始做饭了....
做饭的同时干些其它事情....
做饭无聊时,拍个抖音..... 
吃饭... 
吃饭噎住了... 

2.小结

  1. Promise和 async-await 都是优化异步编程体验的解决方案。
  2. Promise 是应用层的解决方案,它有一个规范,不同的语言也可以实现,它只能异步的处理错误,在js 里它本质上是一个对象。
  3. async-await 是语言层的解决方案,它可以说是 Promise的补充,可以让用户像编写同步代码一样编写异步代码,通过try-catch 可以同步地处理错误。
  4. Promise链式调用会产生回调地狱现象。也不能统一处理异常。
  5. async-await用同步的写法完美地避免了回调地狱,同时方便try-catch捕获异常, async-await 有明确的前后关系,代码可读性更好。