从来没搞过前端后台,学小程序,不懂的地方越来越多,忘得越来越快,记录一下
1. 为什么要Promise?
在JavaScript中,难免会遇到异步访问的场景,比如打开文件,访问数据库等等。如果不做好异步控制,会导致意外结果(比如 返回值还没返回就想调用)因此,一种典型的做法是:
当时,大家把这种情形称之为回调地狱。基于当时的困境,有人提出了Promise,从英文看得出,这是一个承诺,承诺我将来会干什么(其实干的事情就是回调)。
如何表征这个回调?在promise后面跟上then表示要回调的函数即可实现
即:
一个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
4. 小结:
- promise的诞生是为了简化函数嵌套调用流程,也便于后续维护
- 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()