ES8之async/await学习随笔

详细学习参考文档: 阮一峰老师的博客,覆盖知识点ES6/7/8/9,本篇学习笔记对阮老师的关于async/await文档中的知识点进行分点总结

在ES8中加入async/await新特性后,很明显带来的好处是避免回调地狱,代码更加优雅,可读性远远提升。

语法

async函数的语法规则总体上比较简单,难点是错误处理机制。语法总结主要分为以下几点:

1.async函数返回一个 Promise 对象。async函数内部return语句返回的值,会成为then方法回调函数的参数。

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

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

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

async function fun() {
  throw new Error('啊,出错啦');
}

fun().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 啊,出错啦

3.async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有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"

4.正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

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

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

5.另一种情况是,await命令后面是一个thenable对象(即定义then方法的对象),那么await会将其等同于 Promise 对象。

class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  then(resolve, reject) {
    const startTime = Date.now();
    setTimeout(
      () => resolve(Date.now() - startTime),
      this.timeout
    );
  }
}

(async () => {
  const actualTime = await new Sleep(1000);
  console.log(actualTime);
})();

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

async function f() {
  await Promise.reject('出错了');
}

f()
.then(v => console.log(v))
.catch(e => console.log(e))
// 出错了

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

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

错误处理

1.捕获整个async/await函数的错误

async function charCountAdd(data1, data2) {
    const d1=await charCount(data1);
    const d2=await charCount(data2);
    return d1+d2;
}
charCountAdd('Hello','Hi')
    .then(console.log)
    .catch(console.log);//捕捉整个async/await函数的错误

这种方式可以捕捉整个charCountAdd运行过程中出现的错误,错误可能是由charCountAdd本身产生的,也可能是由对data1的计算中或data2的计算中产生的。

2.捕捉单个的await表达式的错误

async function charCountAdd(data1, data2) {
    const d1=await charCount(data1)
        .catch(e=>console.log('d1 is null'));
    const d2=await charCount(data2)
        .catch(e=>console.log('d2 is null'));
    return d1+d2;
}
charCountAdd('Hello','Hi')
    .then(console.log)
    .catch(console.log);//捕捉整个async/await函数的错误

通过这种方式可以捕捉每一个await表达式的错误,如果既要捕捉每一个await表达式的错误,又要捕捉整个charCountAdd函数的错误,可以在调用charCountAdd的时候加个catch。

3.同时捕捉多个的await表达式的错误

async function charCountAdd(data1, data2) {
    let d1,d2;
    try {
        d1=await charCount(data1);
        d2=await charCount(data2);
    }catch (e){
        console.log('d1 is null');
    }
    return d1+d2;
}
charCountAdd('Hello','Hi')
    .then(console.log);

function charCount(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(data.length);
        }, 1000);
    });
}

async/await的几种应用场景

1.获取异步函数的返回值,异步函数本身会返回一个Promise,所以我们可以通过then来获取异步函数的返回值。

async function charCountAdd(data1, data2) {
    const d1=await charCount(data1);
    const d2=await charCount(data2);
    return d1+d2;
}
charCountAdd('Hello','Hi').then(console.log);//通过then获取异步函数的返回值。
function charCount(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(data.length);
        }, 1000);
    });
}

2.async/await在并发场景中的应用

对于上述的例子,我们调用await两次,每次都是等待1秒一共是2秒,效率比较低,而且两次await的调用并没有依赖关系,那能不能让其并发执行呢,答案是可以的,接下来我们通过Promise.all来实现await的并发调用。

async function charCountAdd(data1, data2) {
    const [d1,d2]=await Promise.all([charCount(data1),charCount(data2)]);
    return d1+d2;
}
charCountAdd('Hello','Hi').then(console.log);
function charCount(data) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(data.length);
        }, 1000);
    });
}

实现原理

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}

spawn函数的实现

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        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); });
  });
}
posted @ 2019-04-12 10:52  林璡  阅读(1029)  评论(0编辑  收藏  举报