之前学习了异步编程相关的Generator函数和Promise对象。ES7(目前处于提案阶段)中提供了更加方便的async来实现异步编程。本篇主要对async的相关知识点进行简单介绍。
JavaScript语言只有一个线程,因此如果有耗时任务必须使用异步编程,将程序分两段执行。执行耗时任务时先执行其他任务,等耗时任务执行完成在回头处理。传统上JavaScript有四种异步处理方式:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
在ES6中新增了Generator函数用来封装异步任务从而提供完整的异步编程解决方案。在ES7中新增了async函数提供了异步编程终极解决方案。
Promise对象解决了嵌套回调的代码冗余,使得代码格式变为链式调用,简化了代码样式和逻辑,但在一定程度上也会造成Promise代码的冗余,Generator函数封装了异步任务,每个异步任务用yield标记,简化了语义,但是Generator需要指定执行器才能自动执行,否则只能手动来一步步执行,使用上有所不便。async函数解决了Generator函数的问题,并且具有更加清晰的语义。async函数很简单,下面来简单介绍下其相关内容。
一、async函数简介
简单来说,async函数是Generator的语法糖(所谓语法糖可以理解为一种更加简单实用的实现方式)。下面是Generator和async函数的对比,假设我们有两个耗时异步操作:读取文件与访问网络。
//Generator函数
var gen = function*(){
var f1 = yield readFile('url/a.txt');
var f2 = yield getDataFromServer("api/data");
console.log(f1.toString());
console.log(f2.toString());
}
//async函数
async function(){
var f1 = await readFile('url/a.txt');
var f2 = await getDataFromServer("api/data");
console.log(f1.toString());
console.log(f2.toString());
}
从结构上可以看出,与Generator函数相比,async函数就是用asycn关键字替代(* )进行函数声明。另外用await代替yield指明异步操作。 另外还有以下区别:
- async内置执行器,只要调用就会立即执行,不同于Generator函数需要next或者co模块才会执行
- 笔记yield与星号*, async和await具有更好的语义。async表示函数里存在异步操作,await表示紧跟其后面的表达会耗时,需要等待结果。
- yield后必须跟Thunk函数或者Promise对象,async或者await后面可以跟Promise对象或者原始类型值
- async函数返回Promise对象可以方便进行下一步操作。比Generator返回的Iterator简便。
async函数属于ES7而不是ES6,仍处于提案阶段,但是我们可以通过Babel转码后使用。
async函数的实现
async可以自动执行,其实就是讲Generator函数和自动执行器包装在了一个函数中。下面是ES6标准入门中async函数的实现代码,记录如下:
async function(args){
}
//等同于
function(args){
return spawn(function*(){
//
});
}
//spawn函数就是作为自动执行器存在的
function spawn(genF){
return new Promise(function(resolve, reject){
var gen = genF();
function step(nextF){
try{
var next = nextF;
}catch(e){
return reject(e);
}
if(next.done){
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v){
step(function(){return gen.next(v);})
},function(e){
step(function(){return gen.throw(e)});
});
}
step(function(){return gen.next(undefined);});
});
}
二、async函数的使用与注意事项
async函数返回一个Promise对象,然后可以通过then方法添加回调函数,当函数执行遇到await时先返回,等到异步操作任务执行完成后再去执行函数体内的内容。下面是一个简单示例:
//从服务器获取数据
async function callServer(url){
var response = await getDataFromServer(url);
return response;
}
callServer("api/data").then(function(response){
console.log(response);
});
几点注意:
- await后面的Promise对象运行结果可能是Reject,因此最好把await放在try…catch中。或者在await后的Promise末尾加catch方法。
- await命令只能用在async函数中,不能用在普通函数中
- await在ES6中是作为保留字存在的,因此在ES6中使用await作为标识符将会报错,ES5中不会。
以上就是关于async函数的简单介绍,总体而言无论是理解还是使用async函数都比较简单,在项目中使用起来也非常方便,配得上异步编程终极解决方案的旗号。。。