写点什么

重学 JS | 异步编程 Promise

用户头像
梁龙先森
关注
发布于: 2021 年 01 月 12 日
重学JS | 异步编程  Promise

我们都知道 JavaScript 是属于“单线程”语言,一次只能完成一件任务,不能同时执行多个 js 代码任务。倘若存在耗时的任务,后续任务只能排队等待,这将拖延整个程序的执行,导致页面卡顿,甚至造成页面假死。为了解决这个问题,JavaScript 语言引入了同步任务和异步任务,本文讲讲异步编程。


callback()回调函数

首先看看采用callback()回调函数处理异步。Ajax 异步请求可能是最常见的异步编程了。看下例子:

$.ajax({	url:'/testurl',  success:function(){   	// 回调处理请求成功  }})
复制代码

倘若存在多个嵌套请求,代码将陷入“回调地域”。导致的问题有:

  1. 代码臃肿,可读性差

  2. 代码耦合度高、可维护性差、难以复用

  3. 回调函数都是匿名函数,不方便复用


下面看看 ES6 的Promise,为异步编程提供了更合理的解决方案。


Promise 生命周期

Promise 对象存在三种状态,进行中(pending),已成功(fulfilled)和已失败(rejected)。Promise 创建时处于pending状态,状态的改变只能两种可能。一种是成功的时候,将pending状态改变为fulfilled;另一种是失败时将状态改变为rejected


Promise 执行顺序

const promise = new Promise((resolve,reject)=>{   console.log(1)   resolve()   console.log(2)})promise.then(res=>{	console.log(3)})console.log(4)promise.then(res=>{	console.log(5)})// 执行输出顺序为:1,2,4,3,5
复制代码
  1. Promise 创建后会立即执行,输出 1。

  2. 执行resolve()函数,触发then()函数指定的回调函数,但是它要等当前线程中的同步代码执行完毕,因此依次输出 2,再输出 4

  3. 当所有同步代码执行完毕,执行then()函数,此时存在两个回调,依次执行输出 3、5。


Promise 处理异常

我们通常建议使用catch()去捕获Promise异常,这就使catch()函数和then()函数成对出现,catch()接收的就是执行rejected()函数传递的参数。

为啥建议采用catch()函数呢?看个例子

Promise.resolve()  .then(res=>{		throw new Error('error')	},err=>{  	console.log('err',err)}).catch(err=>{	 console.log('err',err)})
复制代码

这个例子里then()函数的第二个函数不能捕获第一个函数抛出的异常,而catch()函数能捕获到第一个函数的异常,这就是建议采用catch()处理rejected状态的原因。


Promise.all()

const p = Promise.all([p1,p2,p3,p4])
复制代码

p 的状态由all()函数接收的所有 Promise 状态决定,只有所有接收的 Promise 实例状态都变为 fulfilled,则 p 的状态才会变成fulfilled。其中存在一个rejected状态,则 p 状态为rejected。需要注意的是当接收的 Promise 实例已经定义了catch()函数,则其rejected状态会自行捕获消化,并不会触发Promise.all()函数的catch()函数。

const p1 = new Promise((resolve,reject)=>{	resolve(1)})const p2 = new Promise((resolve,reject)=>{	throw new Error('error')}).catch(err=>err)
Promise.all([p1,p2]).then(res=>{ console.log(res) // [1,'error']}).catch(err=>{ // 此处p2的异常并未被Promise.all捕获,而是自行消化。 // p2执行完catch()函数后,p2的状态实际为fulfilled。 console.log('异常',err)})
复制代码


Promise.race()

const p = Promise.race([p1,p2,p3])
复制代码

如果接收的多个 Promise 实例中任何一个实例变化,那么 p 实例的状态就随之改变,最先改变的那个 Promise 实例返回值将作为实例 p 的回调函数入参。


可以实现这么个场景,Ajax 请求 3s 未收到请求成功响应,则自动处理成请求失败。

const p1 = ajaxGet('testUrl')const p2 = new Promise((resolve,reject)=>{	setTimeout(()=>{  	reject(new Error('request timeout 3s'))  },3000)})const p = Promise.race([p1,p2])
复制代码


Promise.resolve()

将传入的变量转换为 Promise 对象,等价于在 Promise 函数内调用resolve()函数。

Promise.resolve()函数执行后,Promise 的状态会立即变为fulfilled,然后进入then()函数中处理。

Promise.resolve()// 等价于new Promise(resolve=>resolve())
复制代码

Promise.reject()函数一样,只不过进入的是catch()函数。这里不再叙述。


至此,我们大体学习了异步编程之一的 Promise。

发布于: 2021 年 01 月 12 日阅读数: 44
用户头像

梁龙先森

关注

脚踏V8引擎的无情写作机器 2018.03.17 加入

还未添加个人简介

评论

发布
暂无评论
重学JS | 异步编程  Promise