优雅的Promise

异步编程

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

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

顾名思义:

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

以往的异步编程模式

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

setTimeout可以定义一个在指定时间之后会被调度执行的回调函数,这是它被称作异步函数的原因
所以同样的我们可以用setTimeout来进行很多操作:

设置一个异步返回值
失败处理
嵌套异步回调
但是写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被称作回调地狱。

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

正文

综上所述

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

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

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

传入的这个函数本身包含两个参数resolve和reject

new Promise((resolve,reject)=>{

})

注意:resolve和reject本身又是一个函数
因为传入的是函数,所以我们可以简单的写入一个定时器函数

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

	})
})

Promise的三种状态

1.待定(pending)
2.兑现(fulfilled,有时也称为解决,resolve)
3.拒绝(rejected)
待定(pending)就是期约最初始的状态

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

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

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

resolve.then, reject.catch

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

我们只需要记住

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

简单的实例:

new Promise((resolve,seject)=>{
	setTimeout(()={
		reject('Error')
		//resolve('Success')
	},1000)
}).then((res)=>{
	console.log(res)
}).catch((err)=>{
	console.log(err)
})

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

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行');
})

Promise.all 、Promise.race

我们可能有如下需求:

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

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

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

设置两个默认为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

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promse.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})

Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

let wake = (time) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${time / 1000}秒后醒来`)
    }, time)
  })
}

let p1 = wake(3000)
let p2 = wake(2000)

Promise.all([p1, p2]).then((result) => {
  console.log(result)       // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
  console.log(error)
})

**需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

二、Promise.race的使用
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  // 打开的是 'failed'
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值