浅谈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

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

原文地址:https://www.cnblogs.com/zhaosijia----1234/p/8949374.html