• 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>([sS]+)</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); });
      });
    }
    
  • 相关阅读:
    数据缓存/NSURLSession
    NSURLConnection基本使用/多线程断点下载/文件的上传
    HTTP协议/数据安全
    block的概念及基本使用 /block访问外部变量
    NSOperation简单介绍/NSOperation基本操作/自定义NSOperation
    GCD介绍/GCD的基本使用/GCD的常见用法
    NSThread方式创建线程/线程安全/线程间的通信
    面试常见知识点
    新课堂练习题
    线程概述
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/10694665.html
Copyright © 2020-2023  润新知