Promise学习笔记
一、一些基础知识
两种类型的回调函数
同步回调
同步回调立即执行,完全执行完了才结束,不会放入回调队列中
如数组遍历相关的回调函数、Promise 的 excutor 函数
const arr = [1,2,3]
arr.forEach(item=>{
console.log(item)
})
console.log('forEach之后才输出')
输出:1 2 3 forEach之后才输出
异步回调
异步回调不会立即执行,会放入回调队列中将来执行
如定时器回调、ajax 回调、Promise 的成功/失败回调
js 的 Error 处理
错误类型/常见的内置错误
Error:所有错误的父类型
ReferenceError:引用的变量不存在,如 a is not defined
TypeError:数据类型不正确的错误,如 Cannot read property ‘xxx’ of undefined 或 xx.xxx is not a function
RangeError:数据值不在其所允许的范围内,如 递归中没有 return,则 RangeError:Maximun call stack size exceeded
SyntaxError:语法错误
错误处理
捕获错误:try{…}catch(error){…}
抛出错误:throw new Error(‘xxx’)
function func(){
if(...){
}else{
throw new Error('出错') //这里只抛出错误,具体要怎么处理是在控制台输出还是弹窗由下一级处理
}
}
try{
func()
}catch(error){
console.log(error.message) //这里捕获错误在控制台输出
}
错误对象
message 属性:错误相关信息
stack 属性:函数调用栈记录信息
二、Promise
Promise 是 ES6 引入的异步编程的新的解决方案,旧的解决方案是使用回调函数
Promise 是个构造函数,Promise 对象用来封装一个异步操作(如 fs 文件操作、数据库操作、ajax、定时器)并可以获取其成功或失败的结果值
Promise 最大的好处是可以解决回调地狱问题,并在指定回调和错误处理方面更灵活
promise 的状态改变
(1)pending 变为 resolved
(2)pending 变为 rejected
注意:只有上面这 2 中情况,且一个 promise 对象只能改变一次状态
无论变为成功或失败,都会有一个结果数据(value 或 reason)
promise运行流程
为什么使用 Promise
(1)指定回调函数的方式更灵活
旧的使用回调函数必须在启动异步任务前指定
promise:启动异步任务(只要 new 创建了 promise 对象就启动了异步任务) => 返回 promise 对象 => 给 promise 对象绑定回调函数(甚至可以在异步任务结束后指定/多个)
(2)支持链式调用,可以解决回调地狱问题
回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件
回调地狱不便于阅读,也不便于异常处理
解决方案:promise 链式调用,还可异常传透链式调用出错了会找到最后一个 catch 处理异常
终极解决方案:async / await
使用 Promise
API
(1)Promise 构造函数 Promise(excutor){}
excutor 函数:执行器 (resolve,reject)=>{}
resolve 函数:内部定义成功时调用的函数 value => {}
reject 函数:内部定义失败时调用的函数 reason => {}
注意:excutor 会在 Promise 内部立即同步回调,异步操作在执行器中执行,then 也是同步执行的,但 then 里的回调函数异步执行
(2)Promise.protorype.then
方法 (onResolved,onRejected)=>{}
onResolved 函数:成功的回调函数 (value) => {}
onRejected 函数:失败的回调函数 (reason) => {}
注意:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调返回一个新的 promise 对象
(3)Promise.protorype.catch
方法 (onRejected)=>{}
onRejected 函数:失败的回调函数 (reason) => {}
注意:是 then() 的语法糖,相当于 then(undefined,onRejected)
(4)Promise.resolve
方法 (value)=>{}
value 是成功的数据或 promise 对象
如 const p = Promise.resolve(1) //返回一个成功值为 1 的 promise 对象
p.then(value=>{console.log(value)})
注意:该方法返回一个成功/失败的 promise 对象,返回一个成功/失败的 promise 对象的简洁语法
(5)Promise.reject
方法 (reason)=>{}
如 const p = Promise.reject(1) //返回一个失败值为 1 的 promise 对象
p.catch(reason=>{console.log(reason)})
注意:该方法返回一个失败的 promise 对象
(6)Promise.all
方法 (promises)=>{}
promises 包含 n 个 promise 的数组
如 const pAll = Promise.all([p1,p2,p3])
pAll.then(
value=>{...},
reason=>{...}
)
注意:该方法返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
(7)Promise.race
方法 (promises)=>{}
promises 包含 n 个 promise 的数组
如 const pAll = Promise.race([p1,p2,p3])
pAll.then(
value=>{...},
reason=>{...}
)
注意:该方法返回一个新的 promise 对象,第一个完成的 promise 的结果状态就是最终的结果状态
基本使用
new Promise((resolve,reject)=>{ //执行器函数,里面执行异步操作任务
setTimeout(()=>{
if(...){
resolve('成功')
} else{
reject('失败')
}
},1000)
})
p.then(
value=>{ //接收成功的 value 数据 onResolved
console.log(value)
},
reason=>{ //接收失败的 reason 数据 onRejected
console.log(reason)
}
)
如何改变 promise 状态
(1)resolve(value):若当前是 pendding 就变为 resolved
(2)reject(reason):若当前是 pendding 就变为 rejected
(3)抛出异常:若当前是 pendding 就变为 rejected
一个 promise 指定多个成功/失败回调函数(如多次对一个 Promise 对象调用 then),都会调用吗
当 promise 改变对应状态时都会调用
改变 promise 状态和指定回调函数谁先谁后?
(1)都有可能,正常情况下(执行器中是异步任务,resolve()/reject() 在异步任务中)先指定回调再改变状态同时执行回调函数,也可以先改变状态再指定回调
(2)如何先改状态再指定回调
1)再执行器中直接调用 resolve()/reject()
2)延迟更长时间才调用 then()
(3)什么时候才能得到数据?
1)若先指定回调,当状态发生改变时,回调函数就会调用,得到数据
2)若先改变状态,当指定回调时,回调函数就会调用,得到数据
如先指定回调再改变状态
new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1) //后改变状态同时指定数据,异步执行回调函数
},100)
}).then( //先指定回调函数,保存当前指定的回调函数
value => {},
reason =>{}
)
先改变状态再指定回调1
new Promise((resolve,reject)=>{
resolve(1) //先改状态同时指定数据
}).then( //后指定回调函数,异步执行回调函数
value => {},
reason =>{}
)
先改变状态再指定回调2
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve(1) //先改状态同时指定数据
},100)
})
setTimeout(()=>{
p.then( //后指定回调函数
value => {},
reason =>{}
)
},110)
promise.then() 返回的新 promise 的结果状态由什么决定?
简单表达:由 then() 指定的回调函数执行结果决定
详细表达:
(1)若抛出异常,新 promise 变为 rejected,reason 为抛出的异常
(2)若返回非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
(3)若返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
如
new Promise((resolve,reject)=>{
resolve(1)
}).then(
value => {
console.log('onResolved1',value)
},
reason =>{
console.log('onRejected1',reason)
}
)
.then(
value => {
console.log('onResolved2',value)
},
reason =>{
console.log('onRejected2',reason)
}
)
上述代码输出:onResolved1 1 onResolved2 undefined
new Promise((resolve,reject)=>{
reject(1)
}).then(
value => {
console.log('onResolved1',value)
},
reason =>{
console.log('onRejected1',reason)
}
)
.then(
value => {
console.log('onResolved2',value)
},
reason =>{
console.log('onRejected2',reason)
}
)
上述代码输出:onRejected1 1 onResolved2 undefined
promise 如何串联多个操作任务?
(1)promise 的 then() 返回一个新的 promise,可以看成 then() 的链式调用
(2)通过 then 的链式调用串联多个同步/异步任务,其中异步任务要包在一个 promise 里
promise 异常传/穿透
(1)当使用 promise 的 then 链式调用时,可在最后指定失败的回调
当 then 中没有对失败时 reason 的处理时相当于默认执行 reason => {throw reason}
(2)前面任何操作出了异常,都会传到最后失败的回调中处理
中断 promise 链
当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
办法:在回调函数中返回一个 pendding 状态的 promise 对象
return new Promise(()=>{})
三、自定义 Promise
(function(window){
function Promise(excutor){
const self = this //将当前 promise 保存起来,否则调用 resolve 函数中的 this 为 window
self.status = 'pending'
self.data = undefined //用于存储结果数据
self.callbacks = [] //每个元素:{onResolved(){},onRejected(){}}
function resolve(value){
if(self.status !== 'pending'){return }
self.status = 'resolved'
self.data = value
//
if(self.callbacks.length>0){
setTimeout(()=>{ //放入队列中执行所有成功的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
})
}
}
function reject(reason){
if(self.status !== 'pending'){return }
self.status = 'rejected'
self.data = reason
//
if(self.callbacks.length>0){
setTimeout(()=>{ //放入队列中执行所有失败的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
})
})
}
}
//立即同步执行 excutor
try{
excutor(resolve,reject)
}catch(error){ //若执行器抛出异常,promise 对象变为 rejected 状态
reject(error)
}
}
//Promise 原型对象的 then,指定成功或失败的回调函数,返回新的 promise 对象
Promise.prototype.then = function(onResolved,onRejected){
onResolved = typeof onResolved === 'function' ? onResolved : value => value //向后传递成功的 value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} //指定默认的失败回调(实现错误/异常传透的关键点)
const self = this
return new Promise((resolve,reject)=>{
function handle(callback){ //调用指定的回调函数处理,并根据执行结果改变 return 的 promise 的状态
try{
const result = callback(self.data)
if(result instanceof Promise){
result.then( //只有 .then 才能获取到 result 的结果
value =>resolve(value),
reason => reject(reason)
)
//或简写为 result.then(resolve,reject)
}else{
resolve(result)
}
}catch(error){
reject(error)
}
}
if(self.status === 'pending'){
//当前为 pending 状态,将回调函数保存起来,保存的函数里会取调用所保存函数来改变 promise 状态
self.callbacks.push({
onResolved(value){ //这里能接收个 value,但没用
handle(onResolved)
},
onRejected(reason){
handle(onRejected)
},
})
}else if(self.status === 'resolved'){
//当前是 resolved 状态,异步执行 onResolve 并改变 return 的 promise 状态
setTimeout(()=>{
handle(onResolved)
})
}else{
setTimeout(()=>{
handle(onRejected)
})
}
})
}
//Promise 原型对象的 catch,指定失败的回调函数,返回新的 promise 对象
Promise.prototype.catch = function(onRejected){
return this.then(undefined,onRejected)
}
//Promise 函数对象的 resolve 方法,返回一个指定结果的 promise
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
if(value instanceof Promise){
value.then(resolve,reject)
}else{
resolve(value)
}
})
}
//Promise 函数对象的 reject 方法,返回一个指定 reason 的失败的 promise
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
//Promise 函数对象的 all 方法,返回一个 promise,只有当所有 promise 都成功时才成功,否则只要有一个失败就失败
Promise.all = function(promises){
const values = new Array(promises.length) //保存所有成功 value 的数组
let resolvedCount = 0 //保存成功 promise 的数量
return new Promise((resolve,reject)=>{
//遍历获取每个 promise 的结果
promises.forEach((p,index)=>{
Promise.resolve(p).then( //Promise.resolve(p)是处理传入的数组中包含的纯数字
value=>{
resolvedCount++
values[index] = value
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => {
reject(reason)
}
)
})
})
}
//Promise 函数对象的 race 方法,返回一个 promise,其结果由第一个完成(注意不是指位置上的第一个)的 promise 决定
Promise.race = function(promises){
return new Promise((resolve,reject)=>{
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
})
})
}
window.Promise = Promise
})(window)
自定义 Promise 的其他方法
Promise 的 resolveDelay 和 rejectDelay,都返回一个 Promise 对象,它们在指定的时间后才确定结果
Promise.resolveDelay = function(value,time){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
if(value instanceof Promise){
value.then(resolve,reject)
}else{
resolve(value)
}
},time)
})
}
Promise.rejectDelay = function(reason,time){
const self = this
return new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(reason)
},time)
})
}
Promise 的 class 版本
(function(window){
class Promise{
constructor(excutor){
const self = this //将当前 promise 保存起来,否则调用 resolve 函数中的 this 为 window
self.status = 'pending'
self.data = undefined //用于存储结果数据
self.callbacks = [] //每个元素:{onResolved(){},onRejected(){}}
function resolve(value){
if(self.status !== 'pending'){return }
self.status = 'resolved'
self.data = value
//
if(self.callbacks.length>0){
setTimeout(()=>{ //放入队列中执行所有成功的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
})
}
}
function reject(reason){
if(self.status !== 'pending'){return }
self.status = 'rejected'
self.data = reason
//
if(self.callbacks.length>0){
setTimeout(()=>{ //放入队列中执行所有失败的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
})
})
}
}
//立即同步执行 excutor
try{
excutor(resolve,reject)
}catch(error){ //若执行器抛出异常,promise 对象变为 rejected 状态
reject(error)
}
}
//Promise 原型对象的 then,指定成功或失败的回调函数,返回新的 promise 对象
then(onResolved,onRejected){
onResolved = typeof onResolved === 'function' ? onResolved : value => value //向后传递成功的 value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} //指定默认的失败回调(实现错误/异常传透的关键点)
const self = this
return new Promise((resolve,reject)=>{
function handle(callback){ //调用指定的回调函数处理,并根据执行结果改变 return 的 promise 的状态
try{
const result = callback(self.data)
if(result instanceof Promise){
result.then( //只有 .then 才能获取到 result 的结果
value =>resolve(value),
reason => reject(reason)
)
//或简写为 result.then(resolve,reject)
}else{
resolve(result)
}
}catch(error){
reject(error)
}
}
if(self.status === 'pending'){
//当前为 pending 状态,将回调函数保存起来,保存的函数里会取调用所保存函数来改变 promise 状态
self.callbacks.push({
onResolved(value){ //这里能接收个 value,但没用
handle(onResolved)
},
onRejected(reason){
handle(onRejected)
},
})
}else if(self.status === 'resolved'){
//当前是 resolved 状态,异步执行 onResolve 并改变 return 的 promise 状态
setTimeout(()=>{
handle(onResolved)
})
}else{
setTimeout(()=>{
handle(onRejected)
})
}
})
}
//Promise 原型对象的 catch,指定失败的回调函数,返回新的 promise 对象
catch(onRejected){
return this.then(undefined,onRejected)
}
//Promise 函数对象的 resolve 方法,返回一个指定结果的 promise
static resolve = function(value){
return new Promise((resolve,reject)=>{
if(value instanceof Promise){
value.then(resolve,reject)
}else{
resolve(value)
}
})
}
//Promise 函数对象的 reject 方法,返回一个指定 reason 的失败的 promise
static reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason)
})
}
//Promise 函数对象的 all 方法,返回一个 promise,只有当所有 promise 都成功时才成功,否则只要有一个失败就失败
static all = function(promises){
const values = new Array(promises.length) //保存所有成功 value 的数组
let resolvedCount = 0 //保存成功 promise 的数量
return new Promise((resolve,reject)=>{
//遍历获取每个 promise 的结果
promises.forEach((p,index)=>{
Promise.resolve(p).then( //Promise.resolve(p)是处理传入的数组中包含的纯数字
value=>{
resolvedCount++
values[index] = value
if(resolvedCount === promises.length){
resolve(values)
}
},
reason => {
reject(reason)
}
)
})
})
}
//Promise 函数对象的 race 方法,返回一个 promise,其结果由第一个完成(注意不是指位置上的第一个)的 promise 决定
static race = function(promises){
return new Promise((resolve,reject)=>{
promises.forEach((p,index)=>{
Promise.resolve(p).then(
value => {
resolve(value)
},
reason => {
reject(reason)
}
)
})
})
}
}
window.Promise = Promise
})(window)
四、async 与 await
async
async 函数返回值为 promise 对象
promise 对象的结果由 async 函数执行的返回值决定
await 表达式
await 右侧的表达式一般为 promise 对象,但也可以是其他的值,若为其他值则返回该值本身
若表达式为 promise 对象,await 返回的是 promise 成功的值,若返回的 promise 失败,就会抛出异常,要得到失败的结果要用 try…catch
function fun(){
return new Promise((resolve,reject)=>{
reject(1)
})
}
async function fun1(){
try{
const value = await fun()
}catch(error){
console.log('得到失败的结果',value)
}
}
await 必须写在 async 函数中
五、JS 异步之宏队列与微队列
JS 中用来存储执行回调函数的队列包含 2 个不同特定的列队
宏队列:保存待执行的宏任务(一些回调函数),如定时器回调、DOM 事件回调、ajax 回调
微队列:保存待执行的微任务(一些回调函数),如 promise 的回调(如 then 等)、MutationObserver 的回调
JS 执行时会区别这 2 个队列
(1)JS 引擎首先必须先执行所有的初始化同步任务代码
(2)每次准备取出每个宏任务执行前,都要将所有的微任务一个个取出来执行
例子1
const first = () => (new Promise((resolve,reject)=>{
console.log(3)
let p = new Promise((resolve,reject)=>{
console.log(7)
setTimeout(()=>{
console.log(5)
resolve(6)
},0)
resolve(1)
})
resolve(2)
p.then((arg)=>{
console.log(arg)
})
}))
first().then((arg)=>{
console.log(arg)
})
console.log(4)
上述代码输出:3 7 4 1 2 5
例子2
setTimeout(()=>{
console.log(0)
},0)
new Promise((resolve,reject)=>{
console.log(1)
resolve()
}).then(()=>{
console.log(2)
new Promise((resolve,reject)=>{
console.log(3)
resolve()
}).then(()=>{
console.log(4)
}).then(()=>{
console.log(5)
})
}).then(()=>{
console.log(6)
})
new Promise((resolve,reject)=>{
console.log(7)
resolve()
}).then(()=>{
console.log(8)
})
宏队列:【0】
微队列:【2,8,4,6,5】
上述代码输出:1 7 2 3 8 4 6 5 0
注意:链式调用的 .then 何时放入微队列取决于链的上一个 then 啥时候执行,如轮到 4 执行后 5 才放入微队列,2 下一长串结束后 6 才放入微队列
注意:里层的多个链式调用 then 只要有一个放入队列后外层的 then 就可以放入队列
4 放入微队列后后面的 then 就执行了,只是 then 里的 5 等 4 执行了采访如微队列,这样即使 5 还没放入微队列,但 6 上面的 then 都执行完了,所以 6 可以放入微队列