JavaScript中异步从来都不简单,很长一段时间内, 我们都是使用的回调来实现。后来,我们可以使用promise
,现在,我们可以使用async
和await
(以下简称异步函数)来实现异步。
虽然异步函数的出现使得编写异步函数更加容易了,但是同样也存在陷阱,并且对于初学者并不是那么的友好。
在这篇文章的两个部分中,我将和你们一起分享一切你需要知道的关于异步函数的知识。
Asynchronous functions
异步函数包含async
关键字,你可以像普通的函数声明一样使用它:
async function functionName (arguments) {
//Do something asynchronous
}
你也可以使用箭头函数
const functionName = async (arguments) => {
//Do something asynchronous
}
Asynchronous functions always return promise
返回什么值并不重要,使用异步函数返回的值总是一个promise
const getOne = async _ => {
return 1;
}
const promise = getOne();
console.log(promise) // Promise
await关键词
当你调用一个promise
时,你使用then
来进行下一步,就像这样:
const getOne = async _ => {
return 1;
}
getOne()
.then(value => {
console.log(value); // 1
})
await
关键词让你定义reslove
函数,一旦promise
成功了,它就会返回传递到then
函数的参数。
const test = async _ => {
const one = await getOne();
console.log(one);
}
test();
Return await
在返回一个promise
之前,等待是没有必要的,你可以直接返回promise
.
如果你返回一个await
, 你先实现了原来的promise,然后,你又创建一个新的promise
,return await
虽然不影响什么,但是没有必要多此一举。
//Don't need to do this
const test = async _ => {
return await getOne();
}
test()
.then(value => {
console.log(value); // 1
})
//Do this instead
const test = aysnc _ => {
return getOne();
}
test()
.then(value => {
console.log(value); //1
})
注意,如果你不需要await
,你不许要使用异步函数。上面的列子可重写。
//Do this instead
const test = _ => {
return getOne();
}
test()
.then(value => {
console.log(value); // 1
});
Handling errors
如果一个promise
导致了错误,你可以使用catch
来捕捉他
const getOne = aysnc (success = true) => {
if (success) return 1;
throw new Error('failure');
}
getOne(false)
.then(error => console.log(error)); // failure
如果你想在异步函数中处理错误,你需要使用一个try/catch
来捕捉它。
const test = async _ => {
try {
const one = await getOne(false);
}catch (e) {
console.log(e); //failure
}
}
test();
如果你有多个await
关键词,这样写错误处理会变的很丑
const test = async _ => {
try {
const one = await getOne(false);
}catch (e) {
console.log(e); //failure
}
try {
const two = await getTwo(false);
}catch (e) {
console.log(e); //failure
}
try {
const three = await getThree(false);
}catch (e) {
console.log(e); //failure
}
}
test();
但是还有一个更好的办法。
我们知道异步函数总是返回一个promise
,当我们调用一个promise
,我们在catch
中处理错误。这意味这我们可以通过添加catch
来处理错误。
const test = async _ => {
const one = await getOne(false);
const two = await getTwo(false);
const three = await getThree(false);
}
test()
.catch(error => console.log(error));
注意: Promise
方法只会让你捕捉一个错误。
Multiple awaits
await
会阻塞js执行下一行代码,知道promise
的resolve
被执行完成。这可能会降低执行的效率。
我们需要创建一个延迟来在实际中演示,通过sleep
来创建延迟。
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
}
//Using Sleep
console.log('Now');
sleep(1000)
.then(value => console.log('after one second')); // print 'now' immediately, 'after one second' printed in console after 1 second
现在我们来看需要等三个promise
的情况,每一个promise
都有一个一秒的延迟。
const getOne = _ => {
return sleep(1000).then(val => 1)
}
const getTwo = _ => {
return sleep(1000).then(val => 1)
}
const getThree = _ => {
return sleep(1000).then(val => 1)
}
如果你await
这些promises在一行中,你将不得不等待3秒在那些promise全部结束之前。这不是很好因为我们强迫JavaScript等待额外的2秒在做我们需要的之前。
const test = async _ => {
const one = await getOne();
console.log(one);
const two = await getTwo();
console.log(two);
const three = await getThree();
console.log(three);
console.log('Done');
}
test(); //Console shows ‘Now’ immediately. One second later, it shows 1. Another second later, it shows 2. Another second later, it shows 3 and ‘Done’ at the same time.
如果getOne
, getTwo
, getThree
可以同时(simultraneously)执行,你可以节省两秒。你可以使用promise.all
同时执行三个promise
.
const test = async _ => {
const promise = [getOne(), getTwo(), getThree()];
console.log('Now');
const [one, two, three] = await Promise.all(promise);
console.log(one);
console.log(two);
console.log(three);
console.log('Done');
};
Console shows ‘Now’ immediately. After one second, console shows 1, 2, 3, and ‘Done’
欢迎补充。