zoukankan      html  css  js  c++  java
  • 浅谈ES6原生Promise

    Promise是ES6中提供的一个API函数对象,可以来解决异步编程中遇到的回调地狱问题。

    Promise的作用是解决回调金字塔的问题,对于控制异步流程实际上没有起到很大的作用。真正使用Promise对异步流程进行控制,我们还要借助ES6 generator函数。Generator 函数在Promise的基础上加了一个执行器,但是Generator语法也麻烦,不直观)

    Promise的兴起,是因为异步方法调用中,往往会出现回调函数一环扣一环的情况。这种情况导致了回调金字塔问题的出现。不仅代码写起来费劲又不美观,而且问题复杂的时候,阅读代码的人也难以理解。 代码不好阅读也不好维护。
    举例如下:

    db.save(data, function(data){
        // do something...
        db.save(data1, function(data){
            // do something...
            db.save(data2, function(data){
                // do something...
                done(data3); // 返回数据
            })
        });
    });

    假设有一个数据库保存操作,一次请求需要在三个表中保存三次数据。那么我们的代码就跟上面的代码相似了。这时候假设在第二个db.save出了问题怎么办?基于这个考虑,我们又需要在每一层回调中使用类似try...catch这样的逻辑。这个就是万恶的来源,也是node刚开始广为诟病的一点。

    另外一个缺点就是,假设我们的三次保存之间并没有前后依赖关系,我们仍然需要等待前面的函数执行完毕, 才能执行下一步,而无法三个保存并行,之后返回一个三个保存过后需要的结果。

    解决回调深渊的利器Promise

    1.new 出 Promise对象

    2.往promise对象中放一个异步任务

    3.默认异步任务就要去做了,状态为Pending(正在进行)

    4.当异步任务执行成功,状态为Resolved,异步任务失败,状态为Rejected

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

    // 创建了一个promise对象
    //只要new 出来 这个参数函数会马上执行
    // resolve 表示成功的方法  reject表示失败的方法
    const p = new Promise(function(resolve,reject){
      fs.readFile('./a.txt','utf8',(err,data) => {
        if(err){
          // throw err 如果失败,调失败的方法  把错误对象传出去
          reject(err)
        } else {
          // 如果成功,调成功的方法 把成功后的数据传出去
          resolve(data)
        }
      })
    })
       
    
    // 使用promise对象的then方法来获取承诺的结果
    // then 方法的第一个参数是resolve方法  第二个参数是reject方法
    // then方法可以继续往后.then
    p.then(function(data){
      console.log(data)
    },function(err){    // 该方法是Promise内部的reject方法
      console.log(err)
    }) 

    promise解决回调地狱

    //封装一个Promise风格的读取文件(这里主要是学习then方法可以继续往后.then)
    
    pReadFile = function (...args) {  // ...在参数中表示剩余参数
      /*console.log(args)
      console.log(...args)*/ // 这里的...是展开数组的意思
      return new Promise(function (resolve, reject) {
        fs.readFile(...args, (err, data) => {
          if (err) {
            // throw err 如果失败,调失败的方法  把错误对象传出去
           return reject(err)
          } else {
            // 如果成功,调成功的方法 把成功后的数据传出去
            resolve(data)
          }
        })
      })
    }
    
    
    
    // then 方法可以继续往后 then
    // 可以顺序的 .then 然后指定下一个任务
    // 第一个 then 的函数是 Promise 容器的 resolve 函数
    // then 方法中的返回值将传递给下一个 then 方法
    // then 方法中返回一般的数据没有意义
    // 只有当 then 方法返回一个 Promise 对象的时候才有意义
    // 当我们的 then 方法返回一个 Promise 对象的时候,那么下一个 then 函数就会作为该 Promise 对象的 resolve 方法
    
    pReadFile('./a.txt','utf8')
      .then((data) => {
        console.log(data)
        return pReadFile('./b.txt','utf8')
      })
      .then((data) => {
        console.log(data)
        return pReadFile('./c.txt','utf8')
      })
      .then((data) => {
        console.log(data)
      })

    promise处理错误

    方法1.用then的第二个参数函数来捕获错误 只能捕获到 Promise reject 错误 方法2.用Promise.catch来捕获错误 catch 方法可以捕获到 Promise 本身的错误以及 resolve 函数内部的错误

    ```javascript
       .catch(err => {
          console.log('catch err', err)
        })
    ```

    如果只有异步任务,那 Promise 意义不大 但是当你有多个异步任务相互嵌套的时候,则 Promise 就很有意义了 所以你会发现、jQuery、axios 等函数库都同时支持:回调函数 + Promise 两种方式来获取结果 我们建议都使用 Promise ,哪怕只有一个异步任务 我们绝大多数时候都是使用别人封装好的 Promise 风格的方法 API 有时候我们也需要自己来封装。

    我们发现 Promise 也没有想象中的那么好用 还是会出现一堆繁琐的 then 甚至语法不太好理解,尤其是新手。 所以 ECMAScript 2017 标准中制定发布了一个新的方式: 终极的异步编程解决方案:async 函数 有了 async 函数,本质还是异步,但是我们可以像写同步代码一样来写异步代码

    async(本质上是Generator函数的语法糖)

    首先要有一个函数,函数要被标记为async,然后我们就可以在该函数中使用await命令来接收promise函数。

    如果没有await则得到的就是一个promise对象。await会等待后面promise的结果,当后面的promise对象Resolved的时候,那么resolve函数的结果会赋值给await命令前面定义的成员

    所有函数都可以是async函数 (函数声明 函数表达式 匿名函数 属性方法 箭头函数)函数要被标记为 async,然后我们就可以在该函数中使用 await 命令来接收 Promise 的结果,promise是用then来接收结果,async 和 await 必须成对出现

    注意:await后面必须是一个promise对象await 会等待后面 Promise 的结果。async 函数返回值 返回 Promise 对象,如果非 Promise对象,那么会将返回值包装为一个立即 resolve 的 Promise 对象返回由于 async 函数返回的是 Promise 对象,那么我们可以继续在其它的 async 函数中使用 await 来等待 async 函数的结果

    async function main(argument) {
      console.log(2)
      // await 命名后面跟一个 Promise 任务
      // dataA 就是 await 后面的 Promise 任务的 resolve 方法的结果
      // await 等待 Promise 内部 resolve
      const dataA = await pReadFile('./data/a.txt', 'utf8')
      console.log(3)
      console.log(dataA)
    
      const dataB = await pReadFile('./data/b.txt', 'utf8')
      console.log(4)
      console.log(dataB)
    
      const dataC = await pReadFile('./data/c.txt', 'utf8')
      console.log(5)
      console.log(dataC)
    }
    
    console.log(1)
    main()
    console.log(6)

    结果为
    1
    2
    6
    3
    dataA
    4
    dataB
    5
    dataC

    所以是异步的,但是看起来像同步代码一样,调方法拿结果,因为没有回调

  • 相关阅读:
    [git 学习篇] git commit原理 --实践体会
    [git 学习篇]工作区和暂存区
    [git 学习篇] git文件版本回退再学习
    [git 学习篇]版本回退
    [git 学习篇] 修改文件
    [git 学习篇] 提交文件
    [git 学习篇] --创建git创库
    [测试框架学习] 测试框架的结构包含
    [python测试框架] http接口测试框架
    向SharePoint页面添加后台代码
  • 原文地址:https://www.cnblogs.com/zhaosijia----1234/p/8949374.html
Copyright © 2011-2022 走看看