ES6之-优雅的Promise

ES6之-优雅的Promise





前言

提示:再讲正篇之前我们可以简单的了解一下JavaScript的异步编程的历程,这将使你对Promise的理解更深一步,如果足够了解请前往目录正篇

🐖:想要学习Vue,强烈推荐去BiliBili大学学习coderwhy老师的视频课

Vue-coderwhy


异步编程

Promise又叫期约,我们在讲解Promise之前,希望大家先了解一个概念:异步编程

特别是在JavaScript这种单线程事件循环模型中,同步操作与异步操作更是代码所要依赖的核心机制。异步行为是为了优化因计算机量大而时间长的操作。如果在等待其他操作完成的同时,即使运行其他指令,系统也能保持稳定,那么这样就是务实的

顾名思义:

  • 同步:内存中顺序执行的处理指令
  • 异步:类似于系统中断,异步操作经常是必要的,因为强制进程等待一个长时间的操作(同步操作则必须要等)是绝对不可取的


以往的异步编程模式

在早期的JavaScript中,只支持定义回调函数来表明异步操作完成,串联多个异步操作是一个常见的问题,通常需要深度嵌套的回调函数(**回调地狱**


setTimeout可以定义一个在指定时间之后会被调度执行的回调函数,这是它被称作异步函数的原因

function invoke(){
	setTimeout( () => {
    console.log('函数将在两秒后执行');
  },2000)
}
invoke();

所以同样的我们可以用setTimeout来进行很多操作:

  • 设置一个异步返回值
  • 失败处理
  • 嵌套异步回调

但是写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被称作回调地狱

我们当然希望以一种更优雅的方式去处理异步编程
那就是 Promise
总结:
简单来说Promise就是对异步编程进行优雅封装






正篇:

综上:

Promise就是异步编程的解决方案

我们在处理复杂的网络请求或者其他的一些异步操作的时候,同步会导致进程的阻塞,等待进程导致页面无法显示这就太可笑了
并且早期的异步编程代码冗杂可观性极差,这就用到了Promise

优雅的Promise

Promise本身是一个对象,是一个构造函数,它需要传入一个参数,并且这个参数必须是函数

new Promise(参数)
new Promise(() => {
 //用箭头函数的写法传入一个函数
})

传入的这个函数本身包含两个参数resolvereject

new Promise((resolve,reject) => {
    
})

注意resolvereject本身又是一个函数

因为传入的是函数,所以我们可以直接简单的写入一个定时器函数

new Promise((resolve,reject) => {
    setTimeout( ()=> {
        
    })
})



Promise的三种状态

  • 待定(pending)
  • 兑现(fulfilled,有时也称为解决,resolve)
  • 拒绝(rejected)

待定(pending)就是期约最初始的状态

  1. 成功就将待定转换为解决状态
  2. 失败就将待定转换为拒绝状态
  3. 注意一旦转换为两种状态,就再也无法改变,无论落定为哪种状态都是不可逆

有两种过度:pending -> fulfilled或者是pending -> rejected

错误机制:
如果错误已经捕获了,那么错误不会继续传递下去 如果错误没有被捕获,那么错误会隐式传递下去,直到有错误处理函数来捕获这个错误

resolve.then、reject.catch

  • then方法接受一个参数-resolve返回的数据(正常时)
  • catch方法接收一个参数-reject返回的信息(抛出异常)

我们只需要记住

  • resolved(解决)时会执行then(继续)
  • rejected(拒绝)时会执行catch(捕获异常)

简单的实例:

new Promise((resolve,reject) => {
    setTimeout(()=> {
    	//resolve('success')
     	reject('Error Data')
    },1000)
}).then((data)=>{
	console.log(data);
}).catch((data)=> {
	console.log(data);
})

在这里插入图片描述





源码分析:

Promise

我们ctrl点击进入Promise
在这里插入图片描述

在这里插入图片描述
学过Java的不难看出Promise其实是一个类,用来实现 PromiseConstructor这个接口,new Promise其实是new一个对象实例调用构造函数

interface PromiseConstructor

executor是exe的全名:译为可执行,里面定义了resolve和reject两个函数

    new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;





then函数的另外一种使用格式

我们来看源码定义Promise的接口

在这里插入图片描述

注意这个then函数,我们把它单独拿出来分析

then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

这一段和最后一小段是泛型,我们先不管它

then<TResult1 = T, TResult2 = never>

有没有看到后面一大段内容被一个逗号分隔开,这其实本身又是两个函数

第一个函数会在onfulfilled(解决)时候执行

onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,

第二个函数会在onrejected(拒绝)时候执行

onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

| 表示并集,所以这个可以传undefined和null

所以可以写作下面的样式

new Promise((resolve,reject) => {
    setTimeout(()=> {
        //resolve('success')
        reject('Error Data')
    },1000)
}).then(函数1,函数2)

因为本质就是resolve执行then和
reject执行catch
所以分析源码我们就可以把.catch.then方法全部写到.then里,

new Promise((resolve,reject) => {
    setTimeout(()=> {
        //resolve('success')
        reject('Error Data')
    },1000)
}).then(data=>{
    console.log(data)//resolve时执行
},err=>{
    console.log(err)//reject时执行
})

在这里插入图片描述







Promise的链式编程思想

为了方便大家理解,我们用简单的数据或者输出代替复杂的网络请求

假如有一个需求:

我们需要进行多次网络请求时,并对请求回来的数据进行大量代码处理

不使用Promise
new Promise((resolve,reject) => {
    setTimeout(()=> {
        console.log('这里是第一次网络请求回来的数据处理代码');
        console.log('大约有100行');
        
        setTimeout(()=> {
       		 console.log('这里是第二次网络请求回来的数据处理代码');
       		 console.log('大约有100行');
            
       		 setTimeout(() => {
    		      console.log('这里是第三次网络请求回来的数据处理代码')
              	  console.log('大约有100行')
  			},1000)
    	},1000)
    },1000)
})

有没有发现,真的是层层嵌套,你现在可能看似逻辑清晰但把数据操作的代码(正常处理+错误捕获)放进去你就会彻底蒙圈了,深层嵌套+数据逻辑处理

这真的是地狱

我们在来看看使用Promise

我们知道,执行resolve函数会跳转到then()方法,我们把请求回来的数据进行的操作代码全都放到then()方法里面,最后在代码的结尾return
一个Promise实例继续进行链式操作
reslove.then-return 实例-reslove.then重复下去

new Promise((resolve,reject) => {
    setTimeout(()=> {
    	resolve()
    },1000)
}).then(() => {
    console.log('这里是第一次网络请求回来的数据处理代码');
    console.log('大约有100行');
    
    return new Promise((resolve,reject) => {
    	setTimeout(()=> {
    		resolve()
    	 },1000)
		}).then(() => {
   		 console.log('这里是第二次网络请求回来的数据处理代码');
   		 console.log('大约有100行');
        
  		 return new Promise((resolve,reject) => {
    		 setTimeout(()=> {
    			resolve()
   			 },1000)
		    }).then(() => {
             console.log('这里是第三次网络请求回来的数据处理代码');
             console.log('大约有100行');
})

这就是所谓的链式编程,从嵌套到链式
它的主要特点就是能把原来对异步编程嵌套的写法进行抽离和分离

这种代码可以将数据处理的代码单独放在then或catch里,乍一看可能觉得还没上面好,那是因为对Promise应用的还不熟练,当你开始使用才会发现它的优雅性是名不虚传




Promise.all

我们举一些简单的实例来讲解

我们可能有如下需求:

我们现在有多个网络请求,并且需要所有请求结果都拿到之后进行下一步操作

我们想要判断两个请求都拿到了,该怎么做呢,平常思维:

并且我们并不能判断哪一个网络请求先拿到,如果已经清楚第一个先拿第二个后拿,那我们只需要在第二个做统一处理就好了,但此时我们只能两个都做处理

设置两个默认为false的变量,某一个请求拿到之后赋值为true,最后做判断两个变量同时为true时进行下一步操作

let isResult1 = false
  let isResult2 = false
//请求1
  $.ajax({
    url:'',
    success:function(){
      console.log('结果1');
      isResult1 = true
      handleResult()
    }
  })
//请求2
  $.ajax({
    url:'',
    success:function(){
      console.log('结果2');
      isResult2 = true
      handleResult()
    }
  })
  function handleResult(){
    if(isResult1&&isResult2 ){
      //同时为true继续执行操作
    }
  }
我们再来看看Promise.all

我们看它的源码可以知道,values:后面看着像数组,不过官方给出的叫可迭代对象(可遍历,因此传入可以是数组)

在这里插入图片描述

Promise.all的特点就在于,你把多个网络请求作为数组写入,它内部会帮你自动判断这几个网络请求操作是否都完成,如果全都完成则会帮你执行.then

Promise.all([
      new Promise((reslove,reject) => {
        $.ajax({
          url:'url1',
          success:function(data){
            resolve(data)
          }
        })
      }),
    new Promise((reslove,reject) => {
      $.ajax({
        url:'url2',
        success:function(data){
          resolve(data)
        }
      })
    })//then函数里面传入的是一个数组,并且数组里面包含着两个请求的结果
  ]).then(array => {
      array[0]
      array[1]
    //此时就可以继续对其操作
    console.log('array[0]','array[1]')
  })
是不是比上面的操作简单的多,你学废了吗
评论 49
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请对长亭晚

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值