Promise的心得
promise
是什么?
从抽象的角度来说,它是一种新的解决方案。是解决JS
中进行异步编程的解决方案。旧的解决方案就是:纯回调(但是promise
里面也是有回调函数的,所以加了个纯
),所以就有回调地狱产生。
从语法上来说: Promise
是一个构造函数。所以就可以创造出实例对象,然后指挥实例对象去做事情。反之,如果是一般的函数,则是函数本身去做事情。
从功能上来说: promise
对象用来封装一个异步操作并可以获取其结果
promise
的状态改变:
-
pending
变为resolved
-
pending
变为rejected
- 只有这 2 种状态
-
pending
表示初始化状态 - 且一个
promise
对象只能改变一次 - 无论变为成功还是失败, 都会有一个结果数据
- 成功的结果数据一般称为
vlaue
, 失败的结果数据一般称为reason
promise
的基本流程:
描述:
- 新建一个新的
promise
对象,而新建的promise
对象的状态就是peding
状态,即初始化状态 - 给
promise
构造函数传递一个参数,这是参数就是执行器函数
,并在执行器函数
中执行异步任务 - 执行异步任务,最终是有可能成功,也有可能失败。如果成功了,则去执行
resolve()
方法,然后promise
对象的状态就变为了resolved
状态;如果失败了,则去执行reject()
方法,然后promise
对象的状态就变为了rejected
状态。 - 状态改变后,就去调用成功,或者失败的回调函数
- 成功或者失败的回调函数,就通过
.then
或者.catch
指定。其中,.then
既可以指定成功的回调,也可以指定失败的回调;但是.catch
只能指定失败的回调 - 最终,
.then
又返回了一个新的promise
对象
Promise的基本使用:
为什么用Promise
纯回调:在你真正执行异步任务之前
,你就得把回调函数事先
指定好,也就是要先指定回调函数,然后再启动异步任务,而且必须是这样。
promise
对象:可以在异步任务启动以后,再指定回调函数,但是是在异步任务成功,或者有结果之前再指定回调函数,当然,也可以在异步任务有了结果之后再指定也可以。这也是纯回调做不到的。也就是相对于纯回调函数,promise
对象在指定回调函数的方式上更加的灵活。
总结:promise
指定回调函数的方式更加灵活。一般是在异步任务执行完以后再指定回调函数。
纯回调
:必须在启动异步任务前指定
Promise
构造函数本身接收一个执行器函数excutor
,而执行器函数excutor
里面又接收两个函数,一个是resolve
函数,一个是reject
函数。且执行器函数excutor
是同步回调函数,而真正的异步代码是写在执行器函数excutor
里面的。
Promise
的原型对象有一个then
方法,指定两个参数onResolved, onRejected
,且都是函数类型,更加准确一点是回调函数,一个对应成功的回调,一个对应失败的回调。成功的回调接收的是value
,失败的回调接收的是reason
,且返回的又是一个新的promise
对象。这也是promise
能够链式调用的前提。
promise
支持链式调用,所以才可以解决回调地狱的问题。
什么是回调地狱?
答:回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件。回调地狱会涉及到多个异步操作,而且它是连续串联执行的 。什么是串联执行?假如有异步任务1,异步任务2,异步任务3…,其中第二个异步任务是以第一个异步任务的结果为条件,同样,第三个异步任务是以第二个异步任务为条件。此时就会产生回调的嵌套,代码组织形式也是逐渐向右,这种代码是难以阅读的。然后就是异常处理还要分别去处理,这样也会加大处理难度。这种后续处理麻烦,也不变阅读的情况就是回调地狱。
每得到一个promise
对象,就说明启动了一个异步任务,或者说一个promise
对应一个异步任务。然后通过.then
编码方式就是从上往下执行,类似同步编码,就不是嵌套的形式,也就不会产生回调地狱。然后再处理异常的方式上,只需要在最后处理即可,也就是中间只需要些成功的回调即可。
//2.2. 使用promise的链式调用解决回调地狱
doSomething()
.then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)
在最后处理异常的方式,也被称之为异常传透:在链式调用时,有多个promise
的处理,而无论是任何一个出了问题,都会一层层传传到最下面错误回调的处理。
总结:promise
处理,在编码上是从上往下的方式,阅读也就更加的方便,然后就是异常处理也会更加的方便。不过promise
也不是解决回调地狱最优化的选择, 因为promise
中还会有回调函数,虽然没有回调的嵌套了,但是还会有回调函数。然而用async
和await
就没有回调函数的产生了。
问:回调地狱的缺点?
答:不便于阅读 / 不便于异常处理
问:解决方案?
答:promise
链式调用
问:终极解决方法?
答:async
/await
-
Promise
构造函数: Promise (excutor) {}excutor
执行器函数: 同步执行 (resolve, reject) => {}resolve
函数: 内部定义成功时我们调用的函数 value => {}reject
函数: 内部定义失败时我们调用的函数 reason => {}
说明:excutor
会在Promise
内部执行同步回调,而异步操作是在执行器中执行的 -
Promise.prototype.then
方法: (onResolved, onRejected) => {}onResolved
函数: 成功的回调函数 (value) => {}onRejected
函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功value的成功回调,和用于得到失败reason的失败回调,并且返回一个新的promise对象,这也是pormise
能够支持链式调用的前提。 -
Promise.prototype.catch
方法: (onRejected) => {}onRejected
函数: 失败的回调函数 (reason) => {}
说明:then()
的语法糖, 相当于:then(undefined, onRejected)
-
Promise.resolve
方法: (value) => {}
value: 成功的数据或promise
对象
说明: 返回一个成功/失败的promise
对象,本质是语法糖。
//二者都返回一个promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 100);
})
const p2 = Promise.resolve(2)
-
Promise.reject
方法: (reason) => {}reason
: 失败的原因
说明: 返回一个失败的promise对象,本质是语法糖。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 100);
})
const p3 = Promise.reject(3)
-
Promise.all
方法: (promises) => {}promises
: 包含n
个promise
的数组
说明: 返回一个新的promise
, 只有所有的promise
都成功才成功, 只要有一个失败了就直接失败
const pAll = Promise.all([p1, p2])
pAll.then(
//注意,此次的value是个复数,也就是说最后输出的 values 是个数组,起里面值的顺序和all里面promise数组一致,和执行完成的先后顺序没有关系,不过用Promise.race时,和执行完成的先后顺序有关系
values => {
console.log('all onResolved()', values)
},
reason => {
console.log('all onRejected()', reason)
}
)
-
Promise.race
方法: (promises) => {}promises
: 包含n
个promise
的数组
说明: 返回一个新的promise
, 第一个完成的promise
的结果状态就是最终的结果状态
//根据race这个单词的意思可知,这个方法更强调的是谁先完成,我就先返回它的值
const pRace = Promise.race([p1, p2, p3])
pRace.then(
value => {
console.log('race onResolved()', value)
},
reason => {
console.log('race onRejected()', reason)
}
)
回调函数:你定义的;你没有亲自调用;但是最终却执行了
promise几个关键问题
- 如何改变promise的状态?
- resolve(value): 如果当前是pendding就会变为resolved
- reject(reason): 如果当前是pendding就会变为rejected
- 抛出异常: 如果当前是pendding就会变为rejected
const p = new Promise((resolve, reject) => {
// resolve(1) // promise变为resolved成功状态
// reject(2) // promise变为rejected失败状态
// throw new Error('出错了') // 抛出异常, promse变为rejected失败状态, reason为 抛出的error
throw 3 // 抛出异常, promse变为rejected失败状态, reason为 抛出的3
})
p.then(
value => {},
reason => {console.log('reason', reason)}
)
p.then(
value => {},
reason => {console.log('reason2', reason)}
)
- 一个
promise
指定多个成功/失败回调函数, 都会调用吗?
答:是的。不过有条件,即当promise
改变为对应状态时都会调用
。
- 改变promise状态和指定回调函数谁先谁后?
答:都有可能。正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调。
-
如何先改状态,再指定回调?
①在执行器中直接调用resolve()/reject()
②延迟更长时间才调用then()
new Promise((resolve, reject) => { resolve(1) //先改变的状态(同时指定数据) }).then(// 后指定回调函数, 异步执行回调函数 value => {console.log('value2', value)}, reason => {console.log('reason2', reason)} ) //验证 .then 里面的回调函数是同步执行,还是异步执行,结果:异步执行 //即,执行器函数里面的 resolve(1)和 .then 都是同步执行,而 .then 里面的 value和 reason 回调函数都是异步执行 console.log('-------')
总结:
promise
的,无论是成功还是失败的回调函数,永远是异步执行的,即使条件已经满足了,即状态改变了,也不会马上执行。 -
如何先指定回调函数, 后改变的状态?常规
// 常规: 先指定回调函数, 后改变的状态 new Promise((resolve, reject) => { setTimeout(() => { resolve(1) // 后改变的状态(同时指定数据), 异步执行回调函数 }, 1000); }).then(// 先指定回调函数, 保存当前指定的回调函数 value => {}, reason => {console.log('reason', reason)} )
-
什么时候才能得到数据?
①如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
②如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
promise.then()
返回的新promise
结果状态由什么决定?(这个最重要)
(1)简单表达: 由then()指定的回调函数执行的结果决定
(2)详细表达:
①如果抛出异常, 新promise变为rejected, reason为抛出的异常
②如果返回的是非promise的任意值, 新promise变为resolved, value为返回的值
③如果返回的是另一个新promise, 此promise的结果就会成为新promise的结果
new Promise((resolve, reject) => {
// resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
// return 2
// return Promise.resolve(3)
// return Promise.reject(4)
throw 5
},
reason => {
console.log('onRejected1()', reason)
// return 2
// return Promise.resolve(3)
// return Promise.reject(4)
throw 5
}
).then(
value => {
console.log('onResolved2()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
promise
如何串连多个操作任务?(有可能是同步任务,也有可能是异步任务)
答:(1)promise
的then()
返回一个新的promise,
可以看成then()
的链式调用
(2)通过then
的链式调用串连多个同步,或者异步任务。但是,如果是异步的,则一定要包在promise
里面,因为promise
就是用来封装异步操作的。(详见第二个.then
内部的异步任务)
new Promise((resolve, reject) => {
resolve(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2 //同步操作
},
).then(
value => {
console.log('onResolved12()', value)
return new Promise((resolve,reject) =>{
//注意:这是一个异步任务,所以必须用promise来封装
setTimeout(() =>{
resolve(3)
},1000)
})
},
).then(
value => {
console.log('onResolved3()', value)
},
)
})
注意:在promise
串联多个操作任务时,第一个promise.then()
里面的回调函数类型,是不会影响第二个promise.then()
里面的回调函数的,能影响第二个promise.then()
里面的回调函数,只能是第一个promise.then()
里面的回调函数的返回值。
打个比方:
new Promise((resolve, reject) =>{
resolve(1)
}).then(
reason =>{
return 2
}
).then(
value =>{
console.log(value)
},
reason =>{
console.log(reason)
}
)
观察上面的代码:在第一个.then()
里面,执行的是失败的回调函数onrejected
,但是它的返回值却不是异常
(throw
,或者 return Promise.resolve()
),也就是虽然执行的是失败的回调,但是在下一个.then
里面执行的却是成功的回调。
promise
异常传/穿透?
(1)当使用promise
的then
链式调用时, 可以在最后指定失败的回调,
(2)前面任何操作出了异常, 都会传到最后失败的回调中处理
- 中断
promise
链?
(1)当使用promise
的then
链式调用时, 在中间中断, 不再调用后面的回调函数
(2)办法: 在回调函数中返回一个pendding
状态的promise
对象
return new Promise(() => {}) // 返回一个pending的promise 中断promise链
补充:箭头函数中的=>
有个作用:return
,前提是=>
的右边没有{}
本文地址:https://blog.csdn.net/qq_41769706/article/details/107522852