Promise对象
前置知识
函数对象与实例对象
- 如何区分函数与对象口诀:括号的左边是函数,点的左边是对象
- 点的左边是对象,其中对象分为:函数对象,实例对象
// 函数对象 Person function Person () {} // 实例对象 person const person = new Person()
两种回调函数
-
回调函数分为两种:同步回调函数和异步回调函数
-
同步回调函数:
- 立即执行
- 不会放入队列中
- 如:forEach函数中的回调函数
const arr = [1, 2, 3] arr.forEach(item => { // 同步回调:立即执行 console.log(item) }) console.log('forEach执行完毕后执行输出')
-
异步回调函数
-
不会立即执行
-
先放入队列中
-
如:定时器,ajax请求
setTimeout(() => { console.log('setTimeout') // 异步回调:先放入队列,等待执行 }, 0) console.log('在定时间结束之前执行')
-
常见的内置错误
-
ReferenceError:引用错误
console.log(a) // ReferenceError: a is not defined
-
TypeError:类型错误
const a = null console.log(a.xxx) // TypeError: Cannot read property 'xxx' for undefined
-
RangeError:范围错误(数据值不在其所允许的范围内)
function fun () { fun() } fun() // RangeError: Maximum call stack size exceeded
-
SyntaxError:语法错误
const text = """" // SyntaxError:Unexpected string
错误的处理-捕获与抛出
-
捕获错误:try…catch
-
抛出错误:throw
// 抛出异常 function fun () { if (new Date.now()%2 == 1) { console.log('ok') } else { // 抛出异常 throw new Error('not') } } // 捕获抛出的异常 try { fun () } catch (err) { alert(err.message) // 弹出框信息为not }
Promise
什么是Promise
- ES6中一个非常重要和好用的特性:Promise
- Promise是JS中进行异步编程的新的解决方案
- 旧的异步编程解决方案为:纯回调形式,容易造成回调地狱问题
- 具体表达:
- 从语法上来说:Promise是一个构造函数
- 从功能上来说:promise对象用来封装一个异步操作并可以获取其结果
- Promise是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
- 是为了解决异步处理回调地狱(也就是循环嵌套的问题)而产生的
- promise的作用:在出现异步操作,特别是异步操作嵌套多层异步操作出现回调地狱时,以一种优雅的方式来处理这些异步,增加代码的可读性和可维护性
Promise的三种状态
-
当开发中有异步操作时,就可以给异步操作包装一个Promise
-
异步操作后会有三种状态
- pending:等待状态(既没有被兑现,也没有被拒绝),比如正在进行网络请求,或者定时器没有到时间
- resolved:满足状态(操作成功完成),当我们主动回调了resolve时,就处于该状态,并且会回调then()
- rejected:拒绝状态(操作失败),当我们主动回调了reject时,就处于该状态,并且回调catch()
-
当 promise 被调用后,它会以处理中状态 (pending)开始。这意味着调用的函数会继续执行,而 promise 仍处于处理中直到解决为止,从而为调用的函数提供所请求的任何数据
-
被创建的 promise 最终会以被解决状态 (resolved) 或 被拒绝状态 (rejected) 结束,并在完成时调用相应的回调函数(传给 then 和 catch)
-
流程图
Promise链式调用
-
无论是then还是catch都可以返回一个Promise对象
-
所以,我们的代码其实是可以进行链式调用的
-
可以直接通过Promise包装了一下新的数据,将Promise对象返回
- Promise.resolve():将数据包装成Promise对象,并且在内部回调resolve()函数(成功之后的回调函数)
- Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数(失败之后的回调函数)
-
既然Promise创建的实例是一个异步操作那么这个异步操作的结果只能有两种状态
- 状态一:异步执行成功 需要在内部调用,成功的回调函数resolve 把结果返回给调用者
- 状态二:异步执行失败了 需要在内部调用,成功的回调函数reject把结果返回给调用者
- 由于Promise的实例是一个异步操作,所以拿到操作结果后,无法使用return把操作结果返回给调用者,这个时候只能使用回调函数的形式,来把成功或者失败的结果返回给调用者
Promise的API
-
Promise构造函数:
Promise (excutor) {}
- excutor():同步执行,函数体中的内容
-
Promise.prototype.then(onResolved, onRejected) => {}
- onResolved():成功时的回调函数
- onRejected():失败时的回调函数
- 说明:返回的是一个新的Promise对象
const p = new Promise() p.then( // onResolved() res => {}, // onRejected() err => {} )
-
Promise.prototype.catch()(onRejected) => {}
- onRejected():失败时的回调函数
- 说明:then()的语法糖,相当于
(undefined, onRejected) => {}
-
Promise.resolve(value)
- value:成功的数据或者(成功/失败)promise对象
-
Promise.reject(value)
- value:失败的数据原因或者(失败)promise对象
-
Promise.all(promises)
- promises:为包含多个promise的数组,如:[promise1, promise2, promise3]
- 说明:返回一个新的primose,只有数组内所有的promise全部成功,才会返回成功
-
Promise.race(promises)
- promises:为包含多个promise的数组,如:[promise1, promise2, promise3]
- 说明:返回一个新的primose,为数组内第一个完成的promise
Promise中的关键问题
-
如何改变promise的状态?
resolve()
:pendding状态 -> resolved状态reject()
:pendding状态 -> rejected状态- 抛出异常
throw new Error()
/throw xxx
:pendding状态 -> rejected状态
-
一个promise指定多个成功/失败回调函数,都会调用吗?
- 当promise改变为对应状态时,指定对应状态的回调函数都会调用
-
改变promise状态和指定回调函数顺序谁先谁后?
-
都有可能;正常情况下是先指定回调函数,后改变状态
// 先指定回调函数,后改变状态 new Promise((resolve, reject) => { setTimeOut(() => { resolve() // 用定时器模块异步请求,改变状态 }, 1000) }).then( // then是同步进行调用的 res => { .... }, // 同步执行,先指定成功的回调函数 err => { .... } // 同步执行,先指定失败的回调函数 ) // 先改变状态,后指定回调函数 new Promise((resolve, reject) => { resolve() // 同步执行,先改变promise的状态 }).then( res => { .... }, // 同步执行,后指定成功的回调函数 err => { .... } // 同步执行,后指定失败的回调函数 )
-
-
重点核心:promise链式调用时,promise.then( )执行回调函数所产生的新promise的结果状态由什么决定?
-
由当前then指定回调函数的执行结果决定
// 情况一 new Promise((resolve, reject) => { setTimeOut(() => { resolve(111) // 情况1 }, 1000) }).then( res => { // 此处生成新的promise的状态,又此处的执行结果决定 // 由于此处执行结果未报错,因此promise状态为onResolved,但无返回值 console.log('onResolved1', res) // 输出 onResolved1 111 }, err => { console.log('onRejected1', err) }, ).then( res => { // 执行成功回调函数,无返回值,因此值输出为undefined console.log('onResolved2', res) // 输出 onResolved2 undefined }, err => { console.log('onRejected2', err) }, ) // 情况二 new Promise((resolve, reject) => { setTimeOut(() => { resolve(111) // 情况2 }, 1000) }).then( res => { // 此处生成新的promise的状态,又此处的执行结果决定 // 由于此处执行return,因此promise状态为onRejected,相当于成功返回222 console.log('onResolved1', res) // 输出 onResolved1 111 return 222 // 情况2 }, err => { console.log('onRejected1', err) }, ).then( res => { // 执行成功回调函数 console.log('onResolved2', res) // 输出 onResolved2 222 }, err => { console.log('onRejected2', err) }, ) // 情况三 new Promise((resolve, reject) => { setTimeOut(() => { resolve(111) // 情况3 }, 1000) }).then( res => { // 此处生成新的promise的状态,又此处的执行结果决定 // 由于此处执行throw,因此promise状态为onRejected console.log('onResolved1', res) // 输出 onResolved1 111 throw 333 // 情况3 }, err => { console.log('onRejected1', err) }, ).then( res => { console.log('onResolved2', res) }, err => { // 执行失败回调函数 console.log('onRejected2', err) // 输出 onRejected2 333 }, ) // 情况四 new Promise((resolve, reject) => { setTimeOut(() => { reject(111) // 情况4 }, 1000) }).then( res => { console.log('onResolved1', res) }, err => { // 此处生成新的promise的状态,又此处的执行结果决定 // 由于此处执行结果未报错,因此promise状态为onResolved console.log('onRejected1', err) // 输出 onRejected1 111 }, ).then( res => { // 执行成功回调函数 console.log('onResolved2', res) // 输出 onResolved2 undefined }, err => { console.log('onRejected2', err) }, )
-
-
promise如何串联多个操作任务(同步/异步)?
-
通过链式调用的方式来串联多个任务
-
注意:在串联异步任务时,需要使用new Promise对异步任务进行处理
new Promise ((resolve, reject) => { setTimeOut(() => { console.log('执行任务1(异步)') resolve(111) }, 1000) }).then( res => { console.log('任务1的结果:', res) // 任务1的结果:111 console.log('执行任务2(同步') return 222 } ).then( res => { console.log('任务2的结果:', res) // 任务2的结果:222 // 使用new Promise对异步任务进行处理 new Promise((resolve, reject) => { setTimeOut(() => { console.log('执行任务3(异步)') reject(333) }, 1000) }) } ).then( res => { .... }, err => { console.log('任务3的结果:', res) // 任务3的结果:333 } )
-
-
promise异常传/穿透?
-
当promise进行then的链式调用时,在最后进行catch的回调
-
当前面的任何一个then出现异常,都会传透到最后的catch中进行处理
new Promise((resolve, reject) => { setTimeOut(() => { reject(111) }, 1000) }).then( res => { .... } // 此时即使没有指定失败的回调函数,这里相当于执行了以下操作,err值为111 // err => throw err ).then( res => { .... } // 此时即使没有指定失败的回调函数,这里相当于执行了以下操作,err值为111 // err => throw err ).catch( err => { console.log(err) // 111 } )
-
-
如何中断promise链?(当promise链进行调用时,在过程中进行中断,不在进行后面的promise链调用)
-
在需要中断的promise链位置,返回一个pendding状态的promise对象
new Promise((resolve, reject) => { setTimeOut(() => { resolve(111) }, 1000) }).then( res => { .... }, err => { .... } ).then( res => { .... }, err => { .... } ).catch( err => { .... } // 需要在此处中断promise链调用,则需要返回一个pendding状态的promise对象,以下 return new Promise(() => {}) ).then( res => { .... }, err => { .... } )
-