浅析Promise

浅析Promise

谈到JS的异步操作,往往第一时间会想到Promise。 但是Promise是否等于异步操作呢? 答案是不。

jacascript里的异步操作是指:js引擎线程在执行到该操作时不会立即返回结果(区别于同步操作),而是将其交由其他浏览器线程进行处理,js引擎线程本身继续往下操作。 待其他现场处理完毕将回调函数放进消息队列后再由js引擎线程进行处理。

所以我们可以得出异步操作的核心特征:

  • 非阻塞性,异步操作不会阻塞后续代码的执行。
  • 回调函数,当一个异步操作完成后,他的回调函数会被调用。

我们在工作学习中常用到的promise不能和异步操作画等号,它实则是ES6新增的让我们更优雅简单地书写异步操作的一种方式。

Promise的三种状态

  • pending:初始状态,待定结果
  • resolve:操作成功
  • rejected:操作失败

状态变更方法

Promise为我们提供了两个方法用于改变Promise实例的运行状态。分别是操作成功resolve(), 操作失败reject()

resolve()

resolve()被用来敲定一个promise的成功状态。

如果入参本身就是一个Promise或其他非Promise类型,那么会作为一个Promise被返回;

当入参是一个 thenable 对象,resolve() 将调用其 then() 方法及其两个回调函数;并根据thenable的then()里最先敲定的状态返回一个promise。

实例:

javascript 复制代码
// 传入非promise
const p1 = Promise.resolve(111);
// 传入promise
const p2 = Promise.resolve(new Promise((resolve, reject) => 
	resolve(123)
));
// thenable
const p3 = Promise.resolve({
  then(onFulfill, onReject) {
    onFulfill("已兑现!");
  },
})
// thenable
const p4 = Promise.resolve({
  then(onFulfill, onReject) {
    throw new TypeError("已失败")
    onFulfill("已兑现!");
  },
})

p1.then(val => 
   console.log(val)
).catch(val => 
   console.log(val)
)
p2.then(val => 
   console.log(val)
).catch(val => 
   console.log(val)
)

p3.then(val => 
   console.log(val)
).catch(val => 
   console.log(val)
)

p4.then(val => 
   console.log(val)
).catch(val => 
   console.log(val)
)

返回值

javascript 复制代码
> 111
> 123
> "已兑现!"
> TypeError: 已失败

Promise.reject():

reject()被用来敲定一个promise的失败状态。

入参为该Promise被拒绝的原因或一个promise对象。

与resolve不同的是,如果传入的是一个promise对象,resolve会返回和入参相同的promise,而reject会返回一个新的promise。

嵌套调用

需要注意的是,如果绑定相应回调函数时 Promise 已经确定了状态(在创建时就已经执行了resolve/rejected),该回调函数将被立即调用。

javascript 复制代码
new Promise((resolveOuter) => {
  resolveOuter(
    new Promise((resolveInner) => {
      setTimeout(resolveInner => {
        console.log("inner end")}, 1000);
    }),
  );
}).then(console.log("outer end"));

返回结果

javascript 复制代码
> "outer end"
> "inner end"

通过这个返回结果可以看出就算resolveOuter里新建的Promise没有执行完,但是外部Promise状态确认后里面执行了then方法里的操作。

Promise的基本结构

javascript 复制代码
new Promise((resolve, rejected) => {

})
.then()
.catch()
.finally()

在基本结构里 (resolve, rejected) => {}) 是一个同步任务区,该区域里的代码都是同步执行的。.then() .catch()分别是成功回调和失败回调,这个区域是异步执行的。.finally()是这个promise执行完毕,无论是成功还是失败都会执行的区域,所以它是晚于.then().catch()的。

并发Promise

讲到异步执行,那我们就不得不讨论并发的情况,promise同样对并发有着很好的支持。

Promise.all() :

all()接受一个promise数组或一个字符串,返回一个promise。若有一个promise失败,则立马结束调用并返回最先失败的promise失败原因。如果所有的promise都成功了则返回一个数组,每个元素为该promise的resolve的返回值,且数组顺序与传入顺序一致。

如果传入的数组里有非promise 值,则该值被忽略直接返回,前提是all被resolve返回。

传入的promise都resolve

javascript 复制代码
const p1 = new Promise((resolve, rejected) => {
    setTimeout(resolve("111"), 1000)})

const p2 = 222
const p3 = Promise.resolve(333)

const Pall = Promise.all([p1, p2, p3]).then(values => 
	console.log(values)
)

返回值

javascript 复制代码
[111,222,333]

传入的promise有一个reject

javascript 复制代码
const p1 = new Promise((resolve, rejected) => {
    setTimeout(rejected("111失败"), 1000)
})

const p2 = 222
const p3 = Promise.resolve(333)
const p4 = Promise.reject("444失败")

const Pall = Promise.all([p1, p2, p3, p4]).catch(
    val => {console.log(val)}
)

返回值

javascript 复制代码
 "111失败"

Promise.allSettled() :

allSettled()接受一个promise数组,无论传入的promise是否成功或者失败,都返回一个对象数组且每个对象都包含status:该promise的最终状态,value(resolve)/reason(reject)。

如果传入的数组里有非promise 值,则该值被认为直接resolve并返回。

实例

javascript 复制代码
const p1 = new Promise((resolve, rejected) => {
    setTimeout(rejected("111失败"), 1000)
})
const p2 = 222
const p3 = Promise.resolve(333)
const p4 = Promise.reject("444失败")

const Pall = Promise.allSettled([p1, p2, p3, p4]).then(results => {
	for(const result of results){
      console.log(result)
    }
})

返回值

javascript 复制代码
> Object { status: "rejected", reason: "111失败" }
> Object { status: "fulfilled", value: 222 }
> Object { status: "fulfilled", value: 333 }
> Object { status: "rejected", reason: "444失败" }

Promise.any() :

any()接受一个promise数组,返回一个promise。若有一个promise成功,则立马结束调用并返回最先成功的promise返回值。如果所有的promise都失败了返回一个AggregateError报错,提示All promises were rejected。

如果传入的数组里有非promise 值,则该值被忽略并直接视为resolve。传入空数组时any()会视为直接失败。

传入的promise有resolve时

javascript 复制代码
const p1 = new Promise((resolve, rejected) => {
    setTimeout(rejected("111失败"), 1000)
})
const p2 = 222
const p3 = Promise.resolve(333)
const p4 = Promise.reject("444失败")

const Pall = Promise.any([p1,p2, p3, p4]).then(val => {
	console.log(val)
})

返回值

javascript 复制代码
> 222

传入的promise全为reject时

javascript 复制代码
const p1 = new Promise((resolve, rejected) =>{
    setTimeout(rejected("111失败"), 1000)
})
const p2 = 222
const p3 = Promise.resolve(333)
const p4 = Promise.reject("444失败")

const Pall = Promise.any([p1, p4]).catch(val => {
	console.log(val)
})

返回值

javascript 复制代码
> AggregateError: All promises were rejected

Promise.race():

race()接受一个promise数组作为参数,并返回一个Promise。这个返回的 promise,只会受到第一个完成的promise状态影响,当有一个promise完成时其他promise自动终止。

实例

javascript 复制代码
const p1 = new Promise((resolve, rejected) =>{
    setTimeout(() => {rejected("111失败")}, 1000)
})
const p2 = new Promise((resolve, rejected) =>{
    setTimeout(() => {resolve("222成功")}, 100)
})

const Pall = Promise.race([p1,p2]).then(
    val => {console.log(val)}
).catch(
    val => {console.log(val)}
)

返回值

javascript 复制代码
> "222成功"
相关推荐
JNU freshman17 分钟前
vue 之 import 的语法
前端·javascript·vue.js
剑亦未配妥18 分钟前
Vue 2 响应式系统常见问题与解决方案(包含_demo以下划线开头命名的变量导致响应式丢失问题)
前端·javascript·vue.js
爱吃的强哥21 分钟前
Vue2 封装二维码弹窗组件
javascript·vue.js
凉柚ˇ21 分钟前
Vue图片压缩方案
前端·javascript·vue.js
ByteCraze36 分钟前
秋招被问到的常见问题
开发语言·javascript·原型模式
渣哥1 小时前
从代理到切面:Spring AOP 的本质与应用场景解析
javascript·后端·面试
UIUV1 小时前
JavaScript代理模式实战解析:从对象字面量到情感传递的优雅设计
javascript
摸着石头过河的石头1 小时前
JavaScript 防抖与节流:提升应用性能的两大利器
前端·javascript
_大学牲1 小时前
FuncAvatar: 你的头像氛围感神器 🤥🤥🤥
前端·javascript·程序员
渣哥2 小时前
面试必问:Spring Bean 的作用域类型有多少种?
javascript·后端·面试