zoukankan      html  css  js  c++  java
  • ES7异步操作async函数

    概述

    async函数是Generator函数的语法糖,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await

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

    let fs = require('fs')
    let readFile = function(filename){
      return new Promise(function(resolve, reject){
        fs.readFile(filename, function(error, contents){
          if(error) return reject(error)
          resolve(contents)
        })
      })
    }
    
    // 自定义一个执行器
    function run(task) {
      let iterator = task(); // 生成器函数task的迭代器
      let result = iterator.next(); // 启动任务
      function step() { // 递归调用step函数,保持对next()的调用
        if(!result.done) {
          let promise = Promise.resolve(result.value);
          promise.then(function(value){
            result = iterator.next(value);
            step();
          }).catch(function(error){
            result = iterator.throw(error);
            step();
          })
        }
      }
      step(); // 启动处理过程
    }
    
    let g = function *() {
      let ret1 = yield readFile('./a.json')
      let ret2 = yield readFile('./b.json')
      console.log(ret1.toString(), ret2.toString())
    }
    run(g)
    

    使用Async 函数,依次读取两个文件

    let fs = require('fs')
    let readFile = function(filename){
      return new Promise(function(resolve, reject){
        fs.readFile(filename, function(error, contents){
          if(error) return reject(error)
          resolve(contents)
        })
      })
    }
    
    let asyncReadFile = async function() {
      let ret1 = await readFile('./a.json')
      let ret2 = await readFile('./b.json')
      console.log(ret1.toString(), ret2.toString())
    }
    asyncReadFile()
    

    run()执行器在上一篇文章Promise和异步编程中已经介绍过了,从调用方式上看,两者确实很相似。Async函数对 Generator 函数的改进,主要体现在以下四点:

    1. 内置执行器

    Generator 函数的执行必须靠执行器,就是示例中的run()函数。而Async函数自带执行器,所以Async函数的执行开起来和普通函数一模一样

    语义化

    async-await相比星号和yield的语义更加清晰,async表示函数里有异步操作,await表示等待表达式结果

    适用性广

    async函数的await命令后面,可以是Promise 对象,也可以是原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)

    返回值是Promise

    async函数的返回值是一个Promise,所以可以用then()方法指定下一步操作,而Generator 函数的返回值是 Iterator 对象。

    多种使用形式

    async 函数有多种使用形式

    // 函数声明
    async function test(){}
    
    // 函数表达式
    const test = async function(){}
    
    // 箭头函数
    const test = async () => {}
    
    // 对象的方法
    let obj = {
      async getName(){}
    }
    
    // Class方法
    class Person() {
      async getInfo(url) {
        let response = await fetch(url)
        return await response.text()
      }
    }
    

    语法

    async函数返回一个Promise对象,所以可以使用then()方法添加回调函数,async函数内部return语句返回的值,会成为then方法回调函数的参数

    async function getData(url) {
      let response = await fetch(url)
      let result = await response.text()
      return result
    }
    
    getData('https://86886.wang/api/articles').then(function(result){
      console.log(result)
    })
    

    示例向一个地址请求数据,请求结束后打印返回的数据

    Promise 对象的状态变化

    async函数返回的Promise对象,必须要等到await命令后面的Promise对象执行完成,才会发生发生状态改变,除非遇到return语句或者抛出错误。

    上面的示例中,必须要等待getData内部的await都执行完,才会执行getData的then()方法

    awiat命令

    通常await命令后面是一个Promsie对象,如果不是会被转换成一个立即resolve的Promsie对象

    async function test() {
      return await 'hello' 
    }
    test().then((value) => {
      console.log(value) // hello
    })
    

    await后面的'hello'会被转换成Promise对象,并立即resolve

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

    async function test() {
      return await Promise.reject('bad request')
    }
    test().catch((value) => {
      console.log(value) // bad request
    })
    

    Promise.reject()前面的return可以省略,错误仍然可以被catch接收到。但是如果Promise.resolve()前面的return省略了,then方法是接收不到返回值的

    async函数里,只要有一个awiat语句后的Promsie变成reject,那么整个async函数都会中断执行

    async function test() {
      let r1 = await Promise.reject('bad request')
      let r2 = await Promise.resolve('hello') // 不会执行
    }
    

    如果需要即使异步操作失败也不要中断后面的异步操作,可把await放到try-catch块里

    async function test() {
      try{
        await Promise.reject('bad request')
      }catch(e){}
      return await Promise.resolve('hello') // 正常执行
    }
    
    test().then(v => console.log(v)) // hello
    

    也可以在await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误

    async function test() {
      await Promise.reject('bad request').catch(e => console.log(e))
      return await Promise.resolve('hello') // 正常执行
    }
    
    test().then(v => console.log(v)) 
    // bad request
    // hello
    

    错误处理

    如果async函数内部抛出错误,会导致返回的Promise对象变为reject状态,错误对象会被catch()方法接收到

    async function test() {
      throw new Error('bad request')
    }
    
    test().then((value) => {
      console.log(value)
    },(error) => {
      console.log(error.message) // bad request
    })
    
    async function test() {
      await new Promise(function(resolve, reject){
        throw new Error('bad request')
      })
    }
    
    test().then((value) => {
      console.log(value)
    },(error) => {
      console.log(error.message) // bad request
    })
    

    防止出错的方法前面说过了,将其放在try...catch代码块之中

    注意事项

    1. 由于await命令后面的Promsie对象,运行结果可能是rejected,所以最好把await命令放到try-catch块中
    async function test(url) {
      try{
        await fetch(url)
      }catch(err){
        console.log(err)
      }
    }
    
    // 或者这样
    async function test(url) {
      await fetch(url).catch(err => console.log(err))
    }
    
    1. 对于多个await后面的异步操作,如果不存在继发关系,最好让它们并发执行,这样可以减少耗时
    // 继发
    let ret1 = await getArticles()
    let ret2 = await getTags()
    
    // 并发
    let [ret1, ret2] = await Promise.all([getArticles(), getTags()])
    
    1. async函数在forEach循环中可能会出错,可以使用for-of循环
    // 可能出错,无法知道异步函数是否执行成功
    function test(db) {
      let arr = [{}, {}, {}]
      arr.forEach(async function(item) {
        await db.post(item)
      })
    }
    
    // 不会出错,可以知道异步执行结果
    async function test(db) {
      let arr = [{},{},{}]
      for(let item of arr) {
        await db.post(item)
      }
    }
    

    在forEach中可能出错的原因是,所有异步操作都是并发的,而在for-of中所有异步操作是继发的

    如果确实需要在循环中,并发执行多个异步操作,可以使用Promise.all()方法解决

    async function test(db) {
      let arr = [{}, {}, {}]
      let promises = arr.map(item => db.post(item))
      let results = await Promise.all(promises)
    
      console.log(results)
    }
    
    优秀文章首发于聚享小站,欢迎关注!
  • 相关阅读:
    输入输出重定向
    进程管理
    普通变量_环境变量_环境变量配置文件
    高级文件操作命令_文件查找
    软件包管理_rpm命令管理_yum工具管理_文件归档压缩_源码包管理
    用户管理_组管理_设置主机名_UGO_文件高级权限_ACL权限
    字符串是否包含中文
    SQL 优化
    JS数组
    RedisUtil 工具类
  • 原文地址:https://www.cnblogs.com/yesyes/p/15352379.html
Copyright © 2011-2022 走看看