async函数
ES2017 标准引入了async函数, 使得异步操作变得更加方便. async函数是什么, 用一句话来说, 它就是Generator函数的语法糖
async函数返回一个Promise对象, 可以使用then方法添加回调函数. 当函数执行时, 一旦遇到await就会先返回Promise对象, 等到异步操作完成, 再接着执行函数体内后面的语句
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value,ms){
await timeout(ms);
console.log(value);
}
let theReturnOfAsync = asyncPrint("hello world",2000);
console.log(theReturnOfAsync);

在上面的例子中可以看到async函数返回了一个Promise对象, 而await命令后面是一个Promise对象. 如果不是, 会被转成一个立即resolve的Promise对象
async 函数返回一个Promise对象
async函数内部return语句返回的值, 会成为then方法回调函数的参数, async函数内部抛出的错误会导致返回的Promise对象变为reject状态, 抛出的错误对象会被catch方法回调函数接收到
Promise对象的状态变化
async函数返回的Promise对象必需等到内部所有await命令后面的Promise对象执行完毕才会发生状态改变, 除非遇到return语句或者抛出错误.
也就是说, 只有async函数内部的异步操作执行完, 才会执行then方法指定的回调函数
await命令
正常情况下, await命令后面是一个Promise对象. 如果不是, 会被转为一个立即resolve的Promise对象
只要一个await语句后面的Promise变为reject, 那么整个async函数都会被中断
有时, 我们希望即使前一个异步操作失败, 也不要中断后面的异步操作. 这时可以将第一个await放在try…catch结构里面, 这样不管这个异步操作是否成功, 第二个await都会执行
另一个方法是在await后面的Promise对象后直接添加一个catch方法, 处理前面的错误
async function asyncPrint(value,ms){
await timeout(ms).catch(e => console.log(e));
console.log(value);
}
请求的发生时机
一开始我想当然的认为ES6中的await后面的方法在触发之后, resolve之前, 会继续执行下一个await, 和java的feature.get()一样
如果第一个feature.get() 耗时 10秒, 第二个耗时11秒, 那么第二个feature会在第一个第一个feature结束后1秒结束
然而ES6中的await并不是这样, 下面的代码能说明这个问题
每个await方法耗时20秒, 第二个会在第一个结束20秒之后才结束,一共耗时40秒
function timeout(ms){
return new Promise((resolve)=>{
setTimeout(resolve,ms)
})
}
async function asyncPrint(value){
console.log("start"); // 0 second
await timeout(20000); // 20 seconds
console.log(value);
await timeout(20000); // 40 seconds
console.log("end")
}
let theReturnOfAsync = asyncPrint("hello world");
console.log(theReturnOfAsync);

稍微做一下改动可以得到一个带返回值的版本
function timeout(ms,value) {
return new Promise(resolve => {
setTimeout(resolve, ms, value);
});
}
async function asyncPrint(value) {
console.log("start"); // 0 second
await timeout(2000,"test1").then((value)=>console.log(value)); // 2 seconds
await timeout(2000,"test2").then((value)=>console.log(value)); // 4 seconds
console.log("end");
}
let theReturnOfAsync = asyncPrint("hello world");
console.log(theReturnOfAsync);

使用Promise.all 方法可以保证同时出发多个Promise对象
function timeout(ms,value) {
return new Promise(resolve => {
setTimeout(resolve, ms, value);
});
}
async function asyncPrint(value) {
console.log("start"); // 0 second
Promise.all([timeout(5000,"test1"), timeout(2000,"test2"), timeout(2000,"test3")]).then(function(values) {
console.log(values); // 5 seconds
});
console.log("end");
}
let theReturnOfAsync = asyncPrint("hello world");
console.log(theReturnOfAsync);

第二种同时触发Promise的写法, 需要注意的是
await 关键字后面如果是Promise对象, 就会造成阻塞, 例如
await getPromise() 就会造成阻塞, 因为getPromise()会返回一个Promise对象, 那么下一个await语句就会被迫等到第一个await返回之后才会被触发
下面的写法中因为先进行了三次调用, 然后await promise, 核心点在于, 同时出发3个请求, 然后对promise对象进行await
function timeout(ms,value) {
return new Promise(resolve => {
setTimeout(resolve, ms, value);
});
}
async function asyncPrint(value) {
console.log("start"); // 0 second
let test1 = timeout(5000,"test1");
let test2 = timeout(7000,"test2");
let test3 = timeout(9000,"test3");
let test1_return = await test1;
// 5 seconds
console.log(test1_return);
let test2_return = await test2;
// 7 seconds
console.log(test2_return);
let test3_return = await test3;
// 9 seconds
console.log(test3_return);
console.log("end");
}
let theReturnOfAsync = asyncPrint("hello world");
console.log(theReturnOfAsync);
