吃透Promise

一、概念描述:

Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法(handlers)。 这让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象
一个 Promise有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
resoleved: 意味着操作成功完成。
rejected: 意味着操作失败。

promise对象的状态只能有一次变化,要么就是从pending变为resoleved,并传递成功的数据,要么变为rejected,传递失败的数据,当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:resoleved 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 resoleved 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。

二、参数

executor是带有 resolve 和 reject 两个参数的函数 。Promise构造函数执行时立即调用executor 函数,这两个函数所做的事就两个,一是改变状态,二是将接受到的值作为promise对象的value, resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改为resoleved(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成resoleved,要么调用reject 函数将promise的状态改为rejected。如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略,不要把这里的两个回调函数和要进行的异步操作搞混在一起,通常实际项目中会将resolve回调函数作为入参传递到model中(使用redux的项目),然后在model中如果接口成功,则调用resolve(result),把接口的返回内容(result)作为promise对象的返回值,然后再通过then方法拿到result内容再进行下一步操作;

三、为什么要使用Promise

1、指定回调函数的方式更加灵活:在promise中其实也是存在回调函数的,相比于老的纯回调形式,它必须在执行异步任务之前指定对应的回调函数,但Promise的回调出现在then方法中,它可以先执行异步任务=》返回promise对象,然后再给promise对象绑定回调函数(甚至还可以在异步任务结束后绑定);
2、支持链式调用,可以解决回调地狱问题:回调地狱就是函数嵌套调用,不便于阅读,不便于异常处理(异常处理都是在每个回调函数中各自处理的),而Promise支持.then()的链式调用,书写上从上往下,便于阅读,处理异常是统一在最下方用.catch进行捕获处理

四、常用API

1、Promise.all(iterable)

它是属于函数对象的方法(函数对象就是指Promise构造函数),这个方法返回一个新的promise对象,参数接受一个数组(子promise),一旦有任何一个promise对象失败则立即触发父promise对象的失败回调,把第一个触发失败的promise对象的错误信息作为父promise的失败错误信息,如果数组中所有promise对象的状态都为成功,则会把所有子promise对象的返回值组成数组作为成功回调的返回值,顺序跟参数中的理论顺序保持一致(注意并不是语句的先后顺序,要结合js执行机制来);Promise.all方法常被用于处理多个promise对象的状态集合;
在这里插入图片描述

以上为都成功的情况,下面看个有失败的

在这里插入图片描述

2、Promise.race(iterable)

它是属于函数对象的方法(函数对象就是指Promise构造函数),当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄(then/catch),并返回该promise对象。(简单来说,就是传入的子promise数组中哪个先完成,就返回哪个子promise的结果,注意:不是数组中的穿入位置,要看实际的promise执行完成时间来定
在这里插入图片描述

3、Promise.reject(reason)

它是属于函数对象的方法(函数对象就是指Promise构造函数),返回一个状态为失败的Promise对象,并将给定的失败信息传递给对应的处理方法

4、Promise.resolve(value)

它是属于函数对象的方法(函数对象就是指Promise构造函数),该方法返回一个以 value 值解析后的 Promise 对象

1、如果这个值是个 thenable(即带有 then 方法),返回的 Promise 对象会“跟随”这个 thenable 的对象,采用它的最终状态(指 resolved/rejected/pending/settled)
2、如果传入的 value 本身就是 Promise 对象,则该对象作为 Promise.resolve 方法的返回值返回。
3、其他情况以该值为成功状态返回一个 Promise 对象。

第一种情况:传入的value是thenable对象

在这里插入图片描述

第二种情况:传入的value是promise对象

在这里插入图片描述

第三种情况:正常值传入

在这里插入图片描述

五、总结区

  1. promise构造器中可以写一个回调(默认成功回调),也可以写两个回调(默认第一个函数是成功回调,第二个是失败回调);
  2. 如果构造器中代码将promise对象状态变为失败了(rejected),则会执行promise对象失败的回调,这里有两条路可走,如果你是在在then方法中写两个回调函数,此时它会走第二个回调函数,如果你在then方法中只写了一个回调函数,那么你必须再在then方法的后面.catch()方法,用于接受失败的信息,不然两者都没有的话,它会报错;
  3. 如果你既在then方法中写了两个回调(成功和失败),又在后面写了catch(),那么他会看两者的顺序,谁先谁执行
  4. then方法是为了promise注册回调函数,函数形式是.then((data)=>{}),data为上一个任务的返回结果,then中的函数一定要有return,这个return可以是一个结果或者一个新的promise对象,才可以让后面的then回调接收,不然后面的then回调接受到的值就是undefined;
  5. 有三种方式可以让promise状态变为失败(1、直接reject;2、代码出错等效于reject;3、throw等效于代码出错)
    在这里插入图片描述
    6、promise中的.then()和catch方法都是Promise原型对象的方法(Promise.prototype.then和Promise.prototype.catch)
    7、.then中的回调函数都是异步执行的,即使条件已经满足了(回调函数已定义+状态已改变),也不是马上执行的
    8、无论新建一个promise对象还是通过Promise.resolve(11)、Promise.rejected(22)、then()、catch()方法产生的promise对象,都会涉及返回的promise对象的状态和值问题,接下来我们通过一些小栗子来看下:

1、第一种情况(通过new Promise()生成的promise对象)

在这里插入图片描述

代码解析:调用res成功回调返回1,对象状态为成功

在这里插入图片描述

代码解析:调用成功回调,返回值为一个对象,状态为成功

在这里插入图片描述

代码解析:调用失败回调,返回一个字符串,状态为失败(控制台报错,由于错误没有被捕捉和处理)

在这里插入图片描述

调用失败回调,返回一个字符串,但通过catch捕捉处理了,所以最后返回的promise对象状态为成功,值为那个字符串

2、通过方法返回的promise对象

在这里插入图片描述

代码解析:构造器中状态为成功,并传递值为123,执行then中成功回调,返回刚才的值123,最终promise对象状态为成功,值为123(注意:从上一个promise对象传递过来的参数是上一个对象的值)

在这里插入图片描述

构造器函数执行完,状态改为失败,传递值为123,然后走then方法中的第二个失败回调,由于失败状态已经执行失败回调了,所以最终返回的promise对象状态为成功,值就是最后回调函数中return返回的值“失败”

在这里插入图片描述

构造函数执行完,状态改为失败,传递值为123,然后执行catch(),只要一开始的错误状态受到失败回调或者catch处理了,最终状态都会是成功,值的话,如果执行的回调函数最后有return,那就是return的值,如果没有返回就为underfined,所以为underfined

在这里插入图片描述

构造函数执行完,状态改为失败,传递值为123,然后执行catch(),只要一开始的错误状态受到失败回调或者catch处理了,最终状态都会是成功,值的话,如果执行的回调函数最后有return,那就是return的值,所以这里为123

六、Promise重要知识点及相应例子

demo1

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)

运行结果:
1
2
4
3

解释:Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。

demo2

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {
  throw new Error('error!!!')
})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)

运行结果:

promise1 Promise { <pending> }
promise2 Promise { <pending> }
(node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!!
(node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
promise1 Promise { 'success' }
promise2 Promise {
  <rejected> Error: error!!!
    at promise.then (...)
    at <anonymous> }

解释:promise 有 3 种状态:pending、fulfilled 或 rejected。状态改变只能是 pending->fulfilled 或者 pending->rejected,状态一旦改变则不能再变。上面 promise2 并不是 promise1,而是返回的一个新的 Promise 实例。

demo3

const promise = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})

promise
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
  
运行结果:then: success1

解释:构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,呼应代码二结论:promise 状态一旦改变则不能再变。

demo4

Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((res) => {
    console.log(res)
  })

运行结果:1,2

解释:promise 可以链式调用。提起链式调用我们通常会想到通过 return this 实现,不过 Promise 并不是这样实现的。promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用。

demo5

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start)
})
promise.then((res) => {
  console.log(res, Date.now() - start)
})

运行结果:
once
success 1005
success 1007

解释:promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。

demo6

Promise.resolve()
  .then(() => {
    return new Error('error!!!')
  })
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })
  
运行结果:

then: Error: error!!!
    at Promise.resolve.then (...)
    at ...

解释:.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获,需要改成其中一种:
return Promise.reject(new Error(‘error!!!’))
throw new Error(‘error!!!’)

demo7

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(console.error)

运行结果:
TypeError: Chaining cycle detected for promise #<Promise>
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:667:11)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:607:3

解释:.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。类似于:

demo8

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
  
  运行结果:1

解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。

demo9

Promise.resolve()
  .then(function success (res) {
    throw new Error('error')
  }, function fail1 (e) {
    console.error('fail1: ', e)
  })
  .catch(function fail2 (e) {
    console.error('fail2: ', e)
  })
  
运行结果:
fail2: Error: error
    at success (...)
    at ...

解释:.then 可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数。.catch 是 .then 第二个参数的简便写法,但是它们用法上有一点需要注意:.then 的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的 .catch 可以捕获之前的错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ronychen’s blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值