重学JS | 异步编程 async/await

346 阅读3分钟

这是我参与更文挑战的第9天,活动详情查看:更文挑战

[重学JavaScript系列文章连载中...]

async是ES2017推出的函数,也是异步编程的解决方案,字面是异步的意思,那么使用它就应该不会阻塞后面代码的执行。首先我们来看个日常用法:

// 例子1
function wait(){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
    	console.log('延迟1s')
      resolve('我执行了')
    },1000)
  })
}

async function run(){
  console.log('开始')
  let p = await wait()
  console.log(p)
  console.log('结束')
}

run()
// 依次输出:
// 开始
// Promise<pending>
// 我执行了
// 延迟1s
// 结束

从例子中我们可以总结出:

  1. async关键字放在函数前,表示函数为异步函数
  2. await关键字是等待的意思,需要等后面的表达式执行结束,才往下走
  3. await关键字只能在async函数内部使用

知道了async/await的用法,看到单独使用async函数是什么效果。

// 例子2
async function wait(){
  return 'gogo'
}
wait()
// 输出:Promise{[[PromiseStatus]]: "resolved",[[PromiseValue]]: "gogo"}

wait().then(res=>{
  console.log(res) // 输出:gogo
})

可以看出async()函数返回的是Promise对象,而且Promise有个resolved。我们可以大胆推测,当async()函数有返回值时,会通过Promise.resolve()函数进行封装,返回一个Promise对象。同理,如果内部发生异常,则会使用Promsie.reject()函数进行封装返回。

这里结合例子1的写法,也可以看出,async函数返回的是Promise对象,要获取返回值可以使用await关键字,但需等待await后面表达式执行结束返回。

了解了Async函数的基础用法,那async函数是什么呢?

async函数实际上是Generator函数的语法糖。

// 懒得写例子了,借用下阮一峰老师的例子
const fs = require('fs');

const readFile = function (fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error);
      resolve(data);
    });
  });
};

const gen = function* () {
  const f1 = yield readFile('/etc/fstab');
  const f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

// gen函数用async函数改写,如下:
const asyncReadFile = async function () {
  const f1 = await readFile('/etc/fstab');
  const f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

可以看出,async函数就是将Generator函数的星号替换成async,将yield替换成await,只不过async函数对Generator函数做了改进。

  1. 内置执行器

Generator函数执行必须靠执行器,调用next方法,或者用co模块;而async函数自带执行器,能像普通函数一样直接执行。

  1. 返回值是Promise

async函数返回值是Promise对象,比而Generator函数返回的是Iterator对象方便多。

  1. 更好的语义

async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果

  1. 更广的适用性

co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。

这里关于Generator函数的学习,可以参考下面文章:

异步编程 Generator()函数

至此我们完成了async异步函数的学习。