在 JS 中说到异步,我们肯定能想到最简单的异步方式–回调,和在 Promise
规范;这里,我们就不在说什么函数的回调了,直接说 Promise
,让我们回顾一下 Promise
:
如果对 Promise
不了解,请自行百度,或者看我的另一篇博客:ES6中的Promis的使用方法
1. Promise简介
这里我们就再啰嗦的介绍一下 Promise
对象
Promise
对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。
Promise
对象是由关键字 new
及其构造函数来创建的。构造函数会把一个叫做“处理器函数”(executor function)的函数作为它的参数。这个“处理器函数”接受两个函数 resolve
和 reject
作为其参数。当异步任务顺利完成且返回结果值时,会调用 resolve
函数,而当异步任务失败且返回失败原因(通常是一个错误对象)时,会调用 reject
函数。
Promise 状态:
- pending:初始状态,既不是成功,也不是失败状态
- fulfilled:操作成功
- rejected:操作失败
让我们看一个 Promise
的例子:
function division(fdiv, div) {
return new Promise(function(resolve, reject) {
// 使用延时函数,模拟异步
setTimeout(() => {
if(div === 0) {
// 模拟异常
reject('0不能作为除数!!!')
} else {
// 模拟正常
resolve(fdiv / div);
}
}, 5000);
});
}
var pms = division(100, 10);
pms.then(res => {
console.log(res);
// 10
}).catch(err => {
console.log(err);
})
console.log(pms);
// Promise {<resolved>: 10}
通过 then
传递执行成功和失败的方法,或者统一使用 catch
单独捕捉失败;并且 then
支持链式反映,可以无限连接then
,但是上一个 then
必须要返回Promise
对象,并且可以使用 catch
统一捕获异常。
2. 如何理解async、await
async
即“异步”,而 await
为“等待”;这样就很好理解了,async
用来声明一个异步操作的方法(async function 方法名),而 await
用于等待一个异步任务执行完成。
这里需要注意:await 只能出现在 async 函数中
让我们修改一项上面的demo,使用 async
和 await
完成吧:
function division(fdiv, div) {
return new Promise(function(resolve, reject) {
// 使用延时函数,模拟异步
setTimeout(() => {
if(div === 0) {
// 模拟异常
reject('0不能作为除数!!!')
} else {
// 模拟正常
resolve(fdiv / div);
}
}, 5000);
});
}
async function test(x, y) {
try {
const rtn = await division(x, y);
console.log(rtn);
} catch(e) {
// 经过测试,我们可以发现,当走异常的时候,我们可以使用try...catch...进行异常捕捉
console.log(e);
}
}
test(100, 10); // 10 在try中打印
test(100, 0); // 0不能作为除数!!! 在catch中打印
3. async、await如何执行
async
声明方法为异步操作,await
是一个操作符,即 await
后面是一个表达式。
async
的返回值:
async function test(fdiv, div) {
if(div === 0) {
throw "0不能为除数!!!";
} else {
return fdiv / div
}
}
console.log(test(100, 10));
// Promise {<resolved>: 10}
console.log(test(100, 0));
// Promise {<rejected>: "0不能为除数!!!"}
// Uncaught (in promise) 0不能为除数!!!
从上面可以看出,当调用一个 async
函数时,会返回一个 Promise
对象。根据mdn的解释:
- 当这个
async
函数返回一个值时,Promise
的resolve
方法会负责传递这个值; - 当
async
函数抛出异常时,Promise
的reject
方法也会传递这个异常值。async
函数中可能会有await
表达式,await
表达式会使async
函数暂停执行,直到表达式中的Promise
解析完成后继续执行async
中await
后面的代码并返回解决结果。
注意: await 关键字仅仅在 async function中有效
既然返回的是 Promise
对象,那么如果在外层不能使用 await
关键字获取的情况下,我们就可以使用 then
链来处理解过了:
async function test(fdiv, div) {
if(div === 0) {
throw "0不能为除数!!!";
} else {
return fdiv / div
}
}
// 使用then链处理结果
test(100, 10).then(res => {
console.log(res);
}).catch(err => {
console.log(err);
});
那么如果 async
函数没有返回值,又会怎么样呢;这个很容一想到,因为方法没有返回值得时候,如果声明变量接受,则接收到的值为 undefined
;所以他会返回:Promise.resolve(undefined)
既然 async
和 `` 基本上和 Promise
相似,但是他们有没有区别呢;这里还是有一点区别的,如下:
function division(fdiv, div) {
return new Promise(function(resolve, reject) {
// 使用延时函数,模拟异步
setTimeout(() => {
if(div === 0) {
// 模拟异常
reject('0不能作为除数!!!')
} else {
// 模拟正常
resolve(fdiv / div);
}
}, 5000);
});
}
// 使用await方式实现
async function test1() {
const t = await division(100, 10);
console.log(t);
// 这句代码必须等待await执行完成后,才能执行
console.log("执行这句话")
}
// 使用Promise方式实现
function test2() {
division(100, 10).then(res => {
console.log(res);
});
// 这句代码不用等到then执行完成,就还立刻执行
console.log("执行这句话")
}
test1();
test2();
区别就是这样的,大家根据场景的不同,进行选择使用吧!
4. await 操作符
MDN 是这样描述 await
的:
await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行async function。若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。
起始简单说就是:
async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
按照mdn解释 await
会暂停当前 async
函数执行,并且 await
后面是一个表达式,即这个 await
等待的是一个表达式(这个表达式返回promise 对象或者一个具体的值):
- 假如这个表达式如果返回的是一个
Promise
对象, 那么它的返回值,实际上就是Promise
的回调函数 resolve 的参数,如果这个Promise rejected
了,await
表达式会把Promise
的异常抛出。 - 假如这个表达式如果返回的是一个常量,那么会把这个常量转为
Promise.resolve(xx)
,同理如果没有返回值也是Promise.resolve(underfind)
这里不在列举更多的例子,上面的例子已经包含了,我想表达的一切。
5. 总结
async
用来声明方法是异步方法awiat
用于async
方法之中,用来暂停方法的执行,等待后面表达式给出结果,然后继续执行方法async
函数的返回结果为一个Promise
对象;当async
函数返回一个值的时候,就等于Promise
调用resolve
函数;当async
函数抛出异常的时候,就等于Promise
调用reject
函数。await
操作符用于等待一个Promise
对象,并将Promise
的结果进行转换(成功则将其resolve
中的结果作为返回值,失败则抛出异常,将reject
中的值作为异常信息);如果await
后面不是Promise
对象,则会被转换为Promise.resolve(xx)