async&await

1,847 阅读7分钟

async起什么作用

async 函数返回的是一个Promise对象;如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象(备注:Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例);所以我们可以调用async修饰的函数后,可以使用then进行链式调用

// 示例1:
async function testAsync() {
    return "hello async";
}

const result = testAsync();
console.log(result);	// result是个promise对象 Promise { 'hello async' }


testAsync().then(v => {
    console.log(v);		// 输出 hello async
});

如果 async 函数没有返回值时,很容易想到,它会返回 Promise.resolve(undefined)

async function testAsync(){
	return;
}
let res = testAsync();
console.dir(res)	// Promise { undefined }

联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致;那么下一个关键点就在于await关键字了

await在等待什么

await只能用在async函数内部。

await 表达式会暂停当前async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数的参数作为 await 表达式的值,继续执行 async function

await语法返回值 = await 表达式

await返回值:

若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。

1. await的表达式如果是一个promise,那么await会等待promise执行完毕,将promise的resolve的参数作为await的返回值。(如果表达式promise是链式调用,也就是说有多个then,则awaite将最终then中resolve的参数或者return的值,作为await的返回值)
2. 如果传递给await的值不是一个 Promiseawait直接返回该值
3.Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。(相当于直接throw 错误原因了,需要catch就可以获取到)
// 示例1:
function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();

// 示例2:
async function f2() {
  var y = await 20;
  console.log(y); // 20
}
f2();


// 示例3:
function resolveAfter2Seconds (x) {
  return new Promise(resolve => {
    resolve(x)
  }).then(res => { return res + 30 });
}

async function f1 () {
  var x = await resolveAfter2Seconds(10);
  console.log(x);	// 40
}
let r = f1();
console.log(11111, r);	// promise对象


// 示例4:2222处的x不打印
function resolveAfter2Seconds (x) {
  return new Promise((resolve, reject) => {
    reject(x)
  })
}
async function f1 () {
  var x
  try {
    x = await resolveAfter2Seconds(10);
  } catch (e) {
    console.log('66', e);	// 66 10
  }
  console.dir(2222, x);	// 2222
}
let r = f1();
console.log(11111, r);	// 11111 promise


//示例5:
function resolveAfter2Seconds (x) {
  return new Promise((resolve, reject) => {
    resolve(x)
  }).then(arg => {
    return new Promise((resolve, reject) => {
      return resolve(100)
    })
  })
}
async function f1 () {
  var x = await resolveAfter2Seconds(10);
  console.log(1, x);	// 1 100
}
let r = f1();
console.log(r);		// promise

await执行顺序问题

总结:执行顺序为:先执行await命令之前的代码,遇到await时停止执行async中await命名后的代码,此时执行async函数外部的代码,执行完毕后再接着执行async函数内部的await命令所在行以及之后的代码

// 执行顺序示例:
console.log('aa')				// 第1步输出 aa
async function test(){
	console.log('bb')			// 第2步输出 bb
    const res = await 222
    console.log(res)			// 第4步输出 222
    console.log('dd')			// 第5步输出 dd
    return res
}
test().then( res => console.log(res))	// 第6步输出 222
setTimeout(() => {
	console.log('ff')			// 第7步输出 ff
}, 5000)
console.log('cc')				// 第3步输出 cc


  • 示例1结论:由于await主要等待的是promise的resolve的值作为await本身的返回值,
// 示例1:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await new Promise((resolved, rejected) => {
        setTimeout(() => {
            console.log("time...")
        }, 5000);
        resolved(111);
    });
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});

// 输出:
aa
111
bb
111
// 这里间隔5秒再输出 time...
time...

// 结论:await主要等待的是其后的promise的resolved的返回的值
  • 示例2结论:await等待的主要是resolve的返回参数,所以这里不执行宏任务settimeout,那么resolve没办法返回参数,await也就没办法结束,那么await命令后面的代码也就没办法执行
// 示例2:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await new Promise((resolved, rejected) => {
        setTimeout(() => {
            console.log("time...")
            resolved(111);
        }, 5000);
    });
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});


// 输出:
aa
// 先输出了aa,接着在这里间隔5秒后再输出下面的4项
time...
111
bb
111

// 结论:await主要等待的是其后的promise的resolved的返回的值
  • 示例3结论
// 示例3:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await new Promise((resolved, rejected) => {
        setTimeout(() => {
            console.log("time...")
            resolved(111);
        }, 5000);
    });
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});
console.log('cc');

// 输出:
aa
cc
// 这里间隔5秒
time...
111
bb
111

// 结论:await只会阻塞async函数内部await之后的代码;async函数外部的代码正常执行。

参考阅读:juejin.im/post/684490…

  • 示例4结论:记住await后面跟的是异步操作,就算是之前promise的同步代码;这里还是默认是异步操作都不执行。
// 示例4:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await new Promise((resolved, rejected) => {
        setTimeout(() => {
            console.log("time...")
        }, 5000);
        resolved(111);
    });
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});
console.log('cc');

// 输出:
aa
cc
111
bb
111
// 这里间隔5秒
time...
  • 示例5结论:在async函数内部遇到await命令后停止async函数的执行,相当于遇到的异步操作,这时先去执行async函数外部的同步代码,执行结束后再回到await处继续执行async函数内await命令后的代码
// 示例5:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await 222
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});
console.log('cc');

// 输出:
aa
cc
222
bb
222
  • 示例6结论:由于await其实是promise的异步处理,所以会在宏任务之前执行
// 示例6:
async function getStockPriceByName () {
    console.log("aa")

    const stockPrice = await 222
    console.log(stockPrice);
    console.log("bb")
    return stockPrice;
}

getStockPriceByName().then(function (result) {
    console.log(result);
});

setTimeout(() => {
    console.log("ff");
}, 5000)
console.log('cc');

// 输出:
aa
cc
222
bb
222
// 这里间隔5秒
ff

错误处理

await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

// 示例1:下方代码reject抛出,await前的return加不加效果是一样的
async function f() {
  await Promise.reject('出错了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))

// 输出:
出错了

任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行

// 示例2:
async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); 		// 不会执行;因为第一个await语句状态变成了reject
}

防止出错的方法,也是将其放在try...catch代码块之中

// 示例3:
async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}

批量并发执行

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。多个请求并发执行,可以使用Promise.all方法

// 示例:

let foo = await getFoo();
let bar = await getBar();
// 并发写法:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

async的使用场景

示例1使用promise请求后端接口,我们必须在then里面才可以拿到数据,然后进行处理;也就是说数据初始化依赖promise请求返回的结果;再通俗讲就是我们需要的做的事情依赖某个操作的结果时,可以将依赖的操作写成await,这样就可以了。

具体可以对比下示例1和示例2,体会下

// 示例1:
getLessonList() {
  axios.get('/xxx', {
    params: {
      courseId: yyy
    }
  }).then(res => {
    if (res && res.status && res.data) {
      this.lessonLists = res.data
      this.lessonLists.forEach(item => {
        this.$set(item, 'xxx', false)
      })
      this.$nextTick(() => {
        this.initScroll()
      })
    }
  }).catch(error => {
    console.log('getLessonListError', error)
  })
}

// 示例2:
async getCourseTask(item, courseId, lessonId) {
  try {
    let loadingInstance = this.$loading()
    let res = await axios.get('/xxx', {
      params: {
        xxx: true,
      }
    })
    this.$loading.stop(loadingInstance)
    if (res && res.status && res.data) {
      item.rankUrl = res.data.rankUrl
    }
  } catch (error) {
    console.log('getCourseTaskError', error)
  }
}

思考

// 示例1:
async function ff(){
   let tt =  await fun(2)
    console.log(66666, tt)
}

function fun(arg){
    new Promise((re, rj) => {
        arg +=1
        re(arg)
    }).then(res => res += 10)
    return -1
}
ff() // 输出:66666 -1


// 示例2:
async function ff(){
   let tt =  await fun(2)
    console.log(66666, tt)  
}

function fun(arg){
    return new Promise((re, rj) => {
        arg += 1
        re(arg)
    }).then(res => res += 10)
}
ff() // 输出 66666 13