深入理解Promise、async和await

1. 为什么要Promise?

在JavaScript中,难免会遇到异步访问的场景,比如打开文件,访问数据库等等。如果不做好异步控制,会导致意外结果(比如 返回值还没返回就想调用)因此,一种典型的做法是:

图片A来自阮一峰微博


当时,大家把这种情形称之为回调地狱。基于当时的困境,有人提出了Promise,从英文看得出,这是一个承诺,承诺我将来会干什么(其实干的事情就是回调)。
如何表征这个回调?在promise后面跟上then表示要回调的函数即可实现
即:

图片B来自阮一峰微博


一个promise对象then之后还是promise,因此可以一直then下去,从而实现嵌套回调。之前有了解promise的同学也许会有疑问,那还要catch干嘛?


getData().then(success,fail)表示getData执行后,
如果成功(即resolved ,通过return res或者reslove(res)实现),则回调success函数。
如果失败(即rejectd, 通过 throw res或者reject(res)实现),则回调fail函数。
无论何种,res都将传入到相应的回调函数中。而getData().then(success,null).catch(fail)getData().then(success,fail)的缩略写法,二者是等价的


2. 为什么要async?

从英文字面理解,就是异步的意思,也就是说用async修饰的函数是一个异步函数,用法跟别的函数没区别,仅仅是异步而已。那异步能干什么?

(async function f() {
    console.log('123');
    console.log('321');
    throw '666';
})().then(res=>{
    console.log(res);
},res=>{
    console.log(11);
});

console.log('456');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');

输出结果

123
321
456
6
6
6
6
6
6
11

我们先定义了一个异步函数,然后执行:进入异步函数后,先打印123,然后抛出一个错误。此时,异步函数交出执行权,排在异步函数后面的console.log得以执行,JS是单线程的,直到最后的6打印完毕,才得以处理异步函数抛出的错误,打印11

继续回答上面的问题,async异步能干什么?就是用来修饰函数,使该函数异步执行,不阻碍后续函数的执行
同时我们注意到,async修饰的函数也带有then catch方法,因此,经async修饰的函数也是一个promise

3. 为什么要await?

从英文字面上理解,await就是等待,等待什么?等待await后面的函数执行完毕,异步函数内部再往下执行。await只能放在async中,且只能修饰promise对象

async function f0() {
    console.log('888');
    return 'ha';
}

async function f1() {
    console.log('777');
    return 'haha';
}

(async function f2() {
    console.log('123');
    await f0();
    await f1();
    console.log('321');
    throw '666';
})().then(res=>{
    console.log(res)
},res=>{
    console.log(11)
});

console.log('456');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');

输出结果

123
888
456
6
6
6
6
6
6
777
321
11

可能有同学会有疑问,await不是等待么?要等f0执行完才能执行后面的啊。这句话没错,await f1确实在await f0之后。

但是在f0调用结束之前,这都在f2的执行时间内,直到f0运行到 return ha。注意f0是一个promise,在return ha后将调用f0的then方法,注意这是一个异步方法,此时,函数执行权交出,由下面的console.log获得执行权。

因此,等最后一个6打印完毕,再回来执行f0的then方法,但是代码中没有写,则默认执行then(null,null)

因此,await f1再执行。所以,会依次打印777 321 11

为验证上述说法,我们对上述代码做一下改动:

function ff() {
    console.log('000');
}

async function f0() {
    console.log('888');
    return 'ha';
}

async function f1() {
    console.log('777');
    return 'haha';
}

(async function f2() {
    console.log('123');
    ff();
    ff();
    await f0().then(()=>{console.log('awaiting f0')});
    await f1().then(()=>{console.log('awaiting f1')});
    console.log('321');
    throw '666';
})().then(res=>{
    console.log(res)
},res=>{
    console.log(11)
});

console.log('456');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');
console.log('6');

输出结果

123
000
000
888
456
6
6
6
6
6
6
awaiting f0
777
awaiting f1
321
11

123跟888之间存在000 000是因为,此时还在f2的执行权限内,并没有产生then或者catch,因此不会让出执行权。

可是当await f0执行到return ha的时候,将调用f0的then。这将是一个异步调用,f2执行权交出,下面的console.log得以执行。

输出最后一个6,再回来执行f0的then,打印awaiting f0。因此,后面的awaiting f1同理。

故而,用async/await结构来表达之前的图片B

图片C来自阮一峰微博

4. 小结:

  1. promise的诞生是为了简化函数嵌套调用流程,也便于后续维护
  2. async/await定义了异步函数,并在其内部可通过await等待promise对象,阻塞后续的执行

5. 那async/await跟promise有什么区别

一句话,我感觉这是一个东西

创建promise对象有两种方法,
方法1:

async function f1() {
    console.log('777');
    return 'haha';
//使用return 'sth'表示执行成功,
//使用throw 'sth'则表示执行失败,'sth'将传递给then
}

方法2:

var f1 = function () {
    return new Promise((resolve,reject) => {
        setTimeout(()=>{
            resolve(10);
//使用resolve (sth)表示执行成功,
//使用reject (sth)则表示执行失败,'sth'将传递给then
            console.log('f1')
        },1000)
        }

    )
}

为什么方法2要放在函数里作为返回,主要原因是,new promise之后,新建的对象会立即执行。这不是我们想要的情形,因此封装在函数内作为返回值,这样只在调用时才执行了,比如f1()

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值