优雅的异步编程版本答案async和await解析

什么是aysnc和await

async 函数就是 Generator 函数的语法糖。

 用Generator 函数,依次读取两个文件。


var fs = require('fs');

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

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

写成 async 函数,就是下面这样。


var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

一比较就会发现,async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。

为什么要用async

为了使我们的异步代码,更像同步的代码

aysnc函数解决了什么

在async/await之前,我们有三种方式写异步代码

  1. 嵌套回调

  2. 以Promise为主的链式回调

  3. 使用Generators

但是,这三种写起来都不够优雅,ES7做了优化改进,async/await应运而生,async/await相比较Promise 对象then 函数的嵌套,与 Generator 执行的繁琐(需要借助co才能自动执行,否则得手动调用next() ), Async/Await 可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。

async 函数的优点

async 函数对 Generator 函数的改进,体现在以下三点。

(1)内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。

var result = asyncReadFile();

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

(3)更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。

怎么使用async函数

async函数语法

  • 自动将常规函数转换成Promise,返回值也是一个Promise对象
  • 只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
  • 异步函数内部可以使用await
async function name([param[, param[, ... param]]]) { statements }
name: 函数名称。
param:  要传递给函数的参数的名称
statements: 函数体语句。
返回值: 返回的Promise对象会以async function的返回值进行解析,或者以该函数抛出的异常进行回绝。

 await语法

  • await 放置在Promise调用之前,await 强制后面的代码等待,直到Promise对象resolve,得到resolve的值作为await表达式的运算结果

  • await只能在async函数内部使用,用在普通函数里就会报错

[return_value] = await expression;

expression:  一个 Promise  对象或者任何要等待的值。

返回值:返回 Promise 对象的处理结果。如果等待的不是 Promise 对象,则返回该值本身。

错误处理

在async函数里,无论是Promise reject的数据还是逻辑报错,都会被默默吞掉,所以最好把await放入try{}catch{}中,catch能够捕捉到Promise对象rejected的数据或者抛出的异常

function timeout(ms) {

  return new Promise((resolve, reject) => {

    setTimeout(() => {reject('error')}, ms);  //reject模拟出错,返回error

  });

}

async function asyncPrint(ms) {

  try {

     console.log('start');

     await timeout(ms);  //这里返回了错误

     console.log('end');  //所以这句代码不会被执行了

  } catch(err) {

     console.log(err); //这里捕捉到错误error

  }

}

asyncPrint(1000);

如果不用try/catch的话,也可以像下面这样处理错误(因为async函数执行后返回一个promise)

function timeout(ms) {

  return new Promise((resolve, reject) => {

    setTimeout(() => {reject('error')}, ms);  //reject模拟出错,返回error

  });

}

async function asyncPrint(ms) {

  console.log('start');

  await timeout(ms)

  console.log('end');  //这句代码不会被执行了

}

asyncPrint(1000).catch(err => {

    console.log(err); // 从这里捕捉到错误

});

如果你不想让错误中断后面代码的执行,可以提前截留住错误,像下面

function timeout(ms) {

  return new Promise((resolve, reject) => {

    setTimeout(() => {

        reject('error')

    }, ms);  //reject模拟出错,返回error

  });

}

async function asyncPrint(ms) {

  console.log('start');

  await timeout(ms).catch(err => {  // 注意要用catch

console.log(err) 

  })

  console.log('end');  //这句代码会被执行

}

asyncPrint(1000);

async+await基本结构

function func(a, b) {
    return a + b
}

let ret = func(10, 30);
let ret1 = func(50, 320);
let ret2 = func(1560, 30);
let ret3 = func(10, 3560);
console.log(ret + ret1 + ret2 + ret3);
如果Promise对象也能有对应的方式来接收,写成类似: 
let data1 = await readfilePromise;  // 直接获取成功的数据

async的最终格式如下:  

async function func() {
    let data1 = await promise对象1;
    let data2 = await promise对象2;
    let data3 = await promise对象3;
}
// 相当于让异步函数对象1先执行完毕之后,再执行异步函数对象2,再执行异步函数对象3

注意事项

  1. 如果await后面只写一个基本数据类型,会这个基本数据类型进行包装,包装成一个 Promise 对象

    async function func() {
        let data1 = await 123;
        //1. await后面只写一个基本数据类型 会这个基本数据类型进行包装,包装成一个 Promise 对象
        // 即data1相当于: new Promise((resolve,reject)=>{resolve(123)})
    
        console.log("data1:", data1);   //data1: 123
    
        return data1
        // return await data1
    
    }
    
    let a = func();
    
    a.then((data)=>{
        console.log(data);  //123  接收到上面的返回值Promise对象的执行结果
    });
  2. 如果await后面是一个 Promise,会把 resolve 的值返回

  3. async 函数里面的 await 是异步的,不是同步

    async function func() {
        console.log("start-------");
        let data1 = await readFile(filePath1, "utf-8");
        console.log("end-----------");
        let data2 = await readFile(filePath2, "utf-8");
        let data3 = await readFile(filePath3, "utf-8");
    
        console.log(data1+data2+data3);
    }
    
    console.log("start");
    func();
    console.log("end");
    
    //输出结果依次为:
    //start
    //start-------
    //end
    //end-----------

Guess you like

Origin blog.csdn.net/nxcniuxiangchen/article/details/121800581