vue的学习笔记(15)之Promise知识讲解

一、认识promise

Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。

1、什么是Promise呢?

(1)ES6中一个非常重要和好用的特性就是Promise。

(2)Promise是做什么的呢?
答:Promise是异步编程的一种解决方案。

(3)什么时候我们会来处理异步事件?
答:一种很常见的场景应该就是网络请求了。我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的像1+1=2一样将结果直接返回。往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。但是,当网络请求非常复杂时,就会出现回调地狱。

用一个案例来解释说明一下

2、网络请求的回调地狱

我们来考虑下面的场景:

  • 我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2
  • 我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3
  • 我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4
  • 发送网络请求url4,获取最终的数据data4

在这里插入图片描述
上面的代码有什么问题呢?
正常情况下,不会有什么问题,可以正常运行并且获取我们想要的结果。但是,这样这样的代码难看而且不容易维护。如果里面包含非常复杂的操作的话,一旦有错那么排错将会非常困难,不易于维护。

在现实开发项目中,我们更加期望的是一种更加优雅、方便的方式来进行这种异步操作。本文就是使用Promise来解决这一问题。

二、Promise的基本使用

一般在有异步操作时,使用Promise对这个异步操作进行封装。

1、定时器的异步事件

我们先来看看Promise最基本的语法。这里,我们用一个定时器来模拟异步事件:
在这里插入图片描述
上图的代码是原始的方法,它的意思就是我们通过一个定时函数,在1s后进行相关信息的打印。在打印完后,经过1s又进行另外信息的执行,以此类推。这样不断的嵌套很多业务操作,显然结构不直观,难以维护。

将其换成Promise代码:

//参数本身就是一个函数: 参数 => 函数(resolve, reject)
  //resolve, reject本身又是函数
  // new Promise((resolve, reject) => {
  //   setTimeout(() => {
  //     resolve()
  //   },1000)
  // }).then(() => {
  //     console.log("Hello World");
  //     console.log("Hello World");
  //     console.log("Hello World");
  //     console.log("Hello World");
  //     console.log("Hello World");
  //     console.log("Hello World");
  //
  //     return new Promise((resolve, reject) => {
  //       setTimeout(() => {
  //         resolve()
  //       },1000)
  //     }).then(() => {
  //       console.log("Hello vue");
  //       console.log("Hello vue");
  //       console.log("Hello vue");
  //       console.log("Hello vue");
  //       console.log("Hello vue");
  //       console.log("Hello vue");
  //
  //       return new Promise((resolve, reject) => {
  //         setTimeout(() => {
  //           resolve()
  //         },1000)
  //       }).then(() => {
  //         console.log("Hello vuecli");
  //         console.log("Hello vuecli");
  //         console.log("Hello vuecli");
  //         console.log("Hello vuecli");
  //         console.log("Hello vuecli");
  //         console.log("Hello vuecli");
  //       })
  //
  //     })
  // })

先来看看Promise代码的结构:
在这里插入图片描述

对比两个代码:
首先,下面的Promise代码明显比上面原始的代码看起来还要复杂。
其次,下面的Promise代码中包含的resolve、reject、then、catch具有什么含义?

先不管它有什么含义,先来看看new promise的基本使用:

  //new -> 构造函数(1.保存一些状态信息 2.执行传入的函数(回调函数))
  //在执行我们传入的回调函数时,会传入两个参数:resolve, reject
  //resolve, reject两个参数又是函数
  new Promise((resolve, reject) => {
  //  进行异步操作
    setTimeout(() => {
      resolve("hello vue")
    },1000)
  }).then((data) => {
    console.log(data);
    console.log(data);
    console.log(data);
    console.log(data);
    console.log(data);
  })

在这里插入图片描述

什么时候使用promise呢?
答:有异步操作的时候,我们就用promise包含,当异步操作成功拿到结果的时候,不要再回调函数里面去处理,通过resovle调用一下,将结果传到then里面去做相关的业务处理;当异步操作失败的时候,会拿到错误的信息,将信息拿过来,调用reject函数,然后去到catch()。

new Promise((resolve, reject) => {
  //  进行异步操作
    setTimeout(() => {
      //成功的时候调用resolve
      // resolve("hello vue")
    //  失败的时候调用reject
      reject("error")
    },1000)
  }).then((data) => {
    console.log(data);
    console.log(data);
    console.log(data);
    console.log(data);
    console.log(data);
  }).catch(err => {
    console.log(err);
  })

在这里插入图片描述

总结:
(1)new Promise是创建一个Promise对象
(2)小括号中((resolve, reject) => {})也很明显就是一个函数,而且这里用的是之前刚刚学习过的箭头函数。
(3)resolve, reject指的是什么?

  • 我们先知道一个事实:在创建Promise时,传入的这个箭头函数是固定的(一般我们都会这样写)
  • resolve和reject它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个。
  • 如果是成功的,那么通常我们会调用resolve(messsage),这个时候,我们后续的then会被回调。
  • 如果是失败的,那么通常我们会调用reject(error),这个时候,我们后续的catch会被回调。

2、Promise三种状态

首先, 当我们开发中有异步操作时, 就可以给异步操作包装一个Promise。异步操作之后会有三种状态,接下来介绍这三种状态。

三种状态:

  • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
  • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
  • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()

在这里插入图片描述

补充
promise另一种写法:

<script>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Hello vue')
      reject('Hello vuecli')
    },1000)
  }).then(data => {
    console.log(data);
  },err => {
    console.log(err);
  })
</script>

then()函数里面有两个函数,第一个函数是满足时调用,也就是回调resolve时执行该函数,如果回调reject那么将会执行第二个函数。从源码我们也可以看到他的执行过程:
在这里插入图片描述

三、promise的链式调用

我们在看Promise的流程图时,发现无论是then还是catch都可以返回一个Promise对象。所以,我们的代码其实是可以进行链式调用的:
这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了

  • Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数
  • Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数

在这里插入图片描述

第一种:

<script>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    },1000)
  }).then(() => {
      console.log("Hello World");
      console.log("Hello World");
      console.log("Hello World");
      console.log("Hello World");
      console.log("Hello World");
      console.log("Hello World");

      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve()
        },1000)
      }).then(() => {
        console.log("Hello vue");
        console.log("Hello vue");
        console.log("Hello vue");
        console.log("Hello vue");
        console.log("Hello vue");
        console.log("Hello vue");

        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve()
          },1000)
        }).then(() => {
          console.log("Hello vuecli");
          console.log("Hello vuecli");
          console.log("Hello vuecli");
          console.log("Hello vuecli");
          console.log("Hello vuecli");
          console.log("Hello vuecli");
        })

      })
  })
</script>

第二种:

网络请求:aaa -> 自己处理
//  处理:aaa111 -> 自己处理
//  处理:aaa111222 -> 自己处理
//   new Promise((resolve, reject) => {
//     setTimeout(() => {
//       resolve('aaa')
//     },1000)
//   }).then(res => {
//     //1.自己处理10行代码
//     console.log(res,'第一层的10行处理代码');
//     //2.对结果进行第一次处理
//     return new Promise((resolve, reject) => {
//       resolve(res+'111')
//     })
//   }).then(res => {
//     console.log(res,'第二层的10行处理代码');
//
//     return new Promise(resolve => {
//       resolve(res+'222')
//     })
//   }).then(res => {
//     console.log(res,'第三层的10行代码');
//   })

第三种是第二种的简写:

//  new Promise(resolve=>resolve(结果))简写
  // new Promise((resolve, reject) => {
  //   setTimeout(() => {
  //     resolve('aaa')
  //   },1000)
  // }).then(res => {
  //   //1.自己处理10行代码
  //   console.log(res,'第一层的10行处理代码');
  //   //2.对结果进行第一次处理
  //   return Promise.resolve(res+'111')
  // }).then(res => {
  //   console.log(res,'第二层的10行处理代码');
  //
  //   return Promise.resolve(res+'222')
  // }).then(res => {
  //   console.log(res,'第三层的10行代码');
  // })

第四种是第三种的简写:

  • 如果我们希望数据直接包装成Promise.resolve,那么在then中可以直接返回数据
  • 注意下面的代码中,将return Promise.resovle(data)改成了return data
    结果依然是一样的

简化版代码:

//省略掉Promise.resolve
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('aaa')
    },1000)
  }).then(res => {
    //1.自己处理10行代码
    console.log(res,'第一层的10行处理代码');
    //2.对结果进行第一次处理
    return res+'111'
  }).then(res => {
    console.log(res,'第二层的10行处理代码');

    return res+'222'
  }).then(res => {
    console.log(res,'第三层的10行代码');
  })

如果有错误的情况下:

  // new Promise((resolve, reject) => {
  //   setTimeout(() => {
  //     resolve('aaa')
  //   },1000)
  // }).then(res => {
  //   //1.自己处理10行代码
  //   console.log(res,'第一层的10行处理代码');
  //   //2.对结果进行第一次处理
  //   return Promise.reject(err)
  // }).then(res => {
  //   console.log(res,'第二层的10行处理代码');
  //
  //   return res+'222'
  // }).then(res => {
  //   console.log(res,'第三层的10行代码');
  // }).catch(err=>{
  //   console.log(err);
  // })

还可以手动抛异常

new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('aaa')
    },1000)
  }).then(res => {
    //1.自己处理10行代码
    console.log(res,'第一层的10行处理代码');
    //2.对结果进行第一次处理
    throw 'err message'
  }).then(res => {
    console.log(res,'第二层的10行处理代码');

    return res+'222'
  }).then(res => {
    console.log(res,'第三层的10行代码');
  }).catch(err =>{
    console.log(err);
  })

四、promise的all方法的使用

官方解释:Promise.all() 方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise实例, 那个输入的所有promise的resolve回调的结果是一个数组。这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。

如果有这么个需求,两个网页请求都完成后我们在进行下一步操作,如果用ajax实现该需求,需要使用下面的方法,用变量来记录两个请求是否执行完,只有两个变量都为true就调用函数进行相关业务处理,非常麻烦。

  // 请求一:
  // let isResult1 = false
  // let isResult2 = false
  // $ajax({
  //   url: '',
  //   success: function () {
  //     console.log('result1');
  //     isResult1 = true
  //     handleResult()
  //   }
  // })
  // // 请求二:
  // $ajax({
  //   url: '',
  //   success: function () {
  //     console.log('result2');
  //     isResult2 = true
  //     handleResult()
  //   }
  // })
  //
  // function handleResult() {
  //   if (isResult1 && isResult2) {
  //     //
  //   }
  // }

因此,可以使用promise.all来实现两个异步操作都完成后才调用相关操作

Promise.all([
    // new Promise((resolve, reject) => {
    //   $.ajax({
    //     url: 'url1',
    //     success: function (data) {
    //       resolve(data)
    //     }
    //   })
    // }),
    // new Promise((resolve, reject) => {
    //   $.ajax({
    //     url: 'url2',
    //     success: function (data) {
    //       resolve(data)
    //     }
    //   })
    // })

    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({name: 'victory', age: 18})
      }, 2000)
    }),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({name: 'CEO', age: 19})
      }, 1000)
    })
  ]).then(results => {
    console.log(results);
  })

promise.all进一步学习可参阅官方文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值