async函数

2018-11-11  本文已影响0人  文仔CXKSNLXX

async函数

async函数的含义

简单来说:async 函数就是 Generator 函数的语法糖
下面是两个是Generator和async函数依次读取两个文件的写法

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());
};

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());
};

区别:其实就是把Generator函数的*号换成await。
改进地方:

  1. async有内置执行器,不用依靠co模块才能执行
  2. async有更好的语义,从字面上就可以知道是异步的了
  3. async有更广的适用性,co模块规定,yield命令后面只能是Thunk函数或Promise对象,而async的await命令后main可以是promise对象和原始类型的值
  4. async返回值是promise:Generator返回值是iterator,所以async可以用then进行链式调用

async的基本用法

async返回值是一个Promise对象,可以使用then方法进行链式调用,当函数执行时,遇到await命令就会先返回,直到异步操作完成后,才继续执行函数后面的内容
多种使用方法:

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭头函数
const foo = async () => {};

async函数返回一个Promise对象

async函数内容return语句返回值,会成为then方法回调函数的参数

async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"

async函数内部抛出错误会导致返回的Promise对象状态变为reject。而错误会被catch方法的回调函数接受到

async function f() {
  throw new Error('出错了');
}

f().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 出错了

async函数内部,只有当所有的异步操作执行完了,才会执行then方法指定的回调函数

async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

如上图,getTitle内部中只能当抓取页面,提取文本,匹配页面标题都执行完了才会执行then方法的console.log方法

await命令

await命令后面可以跟Promise对象或原始数据。如下面的await是跟123

async function f() {
  // 等同于
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123

只要异步函数中,await语句后面的Promise编程reject,那么整个函数都会中断执行。如下例子

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

解决方法:把可能出错的代码放进try chatch结构中,这样不管代码有没有出错,后面的代码依旧会执行。例子如下

async function f() {
  try {
    await Promise.reject('出错了');
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

async的错误处理

如果await命令后面的异步操作出错,那么等同于async函数返回的promise对象被reject
防止出错的方法是将其放进try...catch代码中。如

async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了');
    });
  } catch(e) {
  }
  return await('hello world');
}

注意点

如果多个await命令后面的异步操作不存在继发关系,最好让他们同事触发

let foo = await getFoo();
let bar = await getBar();

上面是写成了继承关系,只有当上一个异步操作完成了,下一个才会开始进行
修改方法

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

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

另外,esm模块加载器支持顶层await,即await命令可以不放在async函数里面直接使用
当然,前提你必须使用esm加载器。
例子:

// async 函数的写法
const start = async () => {
  const res = await fetch('google.com');
  return res.text();
};

start().then(console.log);

// 顶层 await 的写法
const res = await fetch('google.com');
console.log(await res.text());
上一篇 下一篇

猜你喜欢

热点阅读