Promise
学习promise前先了解一个概念
1.1区别实列对象与函数的区别
<script>
//Funa这是一个普通的函数
function Funa(){}
//funb这是一个构造函数 是Funa的实例对象(简称对象,此时Funa已经是个韩函数对象)
const funb = new Funa()
//这是时候发现Funa 的constructor属性中已经包含了包含了f funb()
console.log(Funa.prototype)
console.log(Funa.bind({})) //只有函数对象才有.bind(方法)
Funa.call() //Funa是函数对象
//比如果我们的jquery中的$符号就是一个函数对象
$("#dom") //函数对象
$("#dom").get //get就是函数对象的方法
</script>
1.2二中类型的回调韩式
1.2.1同步回调
概念:立即执行、完全执行了才结束、不会放到回调队列中
列子:数组遍历的相关回调函数。promise的exctor函数
const array= [1,2,3,4,5,6,7,8,9]
//这就是一个遍历的立即执行的回调函数
array.forEach(item=>{ //遍历回调,同步回调函数,不会放入队列、一上来就要执行完
console.log("我是遍历的console>>>>>>>",item)
})
console.log("我是遍历之后的console")
//打印结果
//我是遍历的console>>>>>>> 1
//我是遍历的console>>>>>>> 2
//我是遍历的console>>>>>>> 3
//我是遍历的console>>>>>>> 4
//我是遍历的console>>>>>>> 5
//我是遍历的console>>>>>>> 6
//我是遍历的console>>>>>>> 7
//我是遍历的console>>>>>>> 8
//我是遍历的console>>>>>>> 9
//我是遍历之后的console
看的数来同步回调只有上面的forEach执行完了,才会执行下面的console
1.2.3异步回调
概念:不会立即回调、会放到回调队列中将来执行
例子:定时器回调/Ajax回调/promise成功失败的回调
setTimeout(()=>{ //异步回调函数,会放到队列中将来执行
console.log("timeOut callBack")
},0)
console.log("setTimeout之后")
//打印结果
//setTimeout之后
//timeOut callBack
尽管setTimeout的时间为0,但还是执行后面的console
1.3 JS中error的处理
-
错误的处理
Error:所有错误的父类型
RegerenceError:应用类型的变量不存在
TypeError:数据类型不争却的错误
RangeErrror:数据值不再其允的范围内
SyntaxError:语法错误 -
错误处理
捕获错误:try … catch
抛出错误:throw error -
错误对象
message属性:错误相关信息
stack属性:韩式调用栈记录信息
常见的内置错误:
RegerenceError:应用类型的变量不存在
//打印一个不存在的变量
consoel.log(dfsadfdsafadsf)
TypeError:数据类型不正确的错误
//错误的使用
let b = null
console,log(b.xxx)
RangeErrror:数据值不再其允的范围内
function fn(){
fn()
}
fn()
SyntaxError:语法错误
const a = " "" "
1.3.1 try …catch
try{
let d
console.log(d.xxxx)
}catch(error){
console.log(error)
}
自定义抛出错误
function something (){
if (Date.now()%2==1){
console.log("当前时间为奇数,可以执行任务")
}else{ //如果时间不是奇数,由调用者来处理
throw new Error("我是跑出去的错误")
}
}
//捕获处理异常
try {
something()
}catch(error){
alert(error.message)
}
龟龟 学废了嘛?
promise的理解和使用
2.1 promise是什么?
2.1.1 理解
- 抽象表达:promise是js中进行异步编程的新的解决方案,因为js是单线程的,但是在数据请求或者定时器的一些场景中,存在了异步,es6就出现了promise来解决异步。
- 具体表达:promise是一个构造函数、promise对象u哦你过来封装一个异步操作并可以获取其结果
2.1.2 promise 的状态改变
- pending 变为 resolved
- pending 变为 rejected
说明:只有这两种,且一个promise对象只能改变一次,无论成功失败,都会由一个结果数据,success or error
2.1.3 promise 基本的基本流程
2.1.4 promise 的基本使用
//1.创建一个新的Promise对象
const promise = new Promise((resolve, reject) => { //执行器函数
//2.执行异步操作任务
setTimeout(() => {
const time = Date.now() //如果当前时间是偶数就代表成功反之失败
if (time % 2 == 0) {
//3.1 如果成功了,调用resolve(value)
resolve("现在是偶数,进入成功拉,现在的时间是"+time)
} else {
//3.2 如果失败了,调用reject(reson)
reject("失败的数据,失败拉现在的时间是"+time)
}
}, 1000)
})
promise.then(
(value) => { //接受得到成成功的value数据 onResolved
console.log("成功的回调函数",value)
},
(reson) => {//接受得到失败的reason数据 onRejected
console.log("失败的回调函数",reson)
}
)
2.2 为什么要用promise
2.2.1指定回调函数的方式更加灵活
- es6之前
-es6之前是用纯回调函数解决异步,在写异步函数时指定成功和失败的回调,官方文档这样举例:
1.1使用纯回调函数//
createAudioFilrAsync(audioSetting,successCallbanc,failureCallback)
这种写发必须先指定回调函数,再开始异步。
2. 使用promise
1.2 使用promise //
const promise = crrateAudioFlieAsync(audioSettings)//2
setTimeout(()=>{
promise.then(successCallback.failureCallback)
},3000)
promis的优势就是就promise执行完后,得到成功的数据后,再指定成功和失败的回调函数,(指定回调函数的方式更加灵活)
2.2.3支持链式调用,可以解决回调地狱
- 回调地狱
回调地狱就是在多个函数嵌套,比如三个嵌套的函数,第二个函数是以第一个函数的结果为条件、第三个函数以第二个函数的结果为条件、他们是异步任务的话(比如请求数据),
//2.1回调地狱
doSomething(function(result){
doSomethingElse(result,function(newResult){
doThirdThing(newResult,function(finalResult){
console.log('最终结果'+finalResult)
},failureCallback)
},failureCallback)
},failureCallback)
这种纯回调代码难以阅读,处理问题难以维护
4. promise的写法
// 2.2 使用promise的链式调用解决回调地狱的问题
doSomething().then(function(result){
return doSomethingElse(result)
}).then(function(newResult){
return doThirdThing(newResult)
}).then(function(finalResult){
console.log('得到最后的结果'+finalResult)
}).catch(failureCallback)
这种编码方式就是链式调用,更清晰,异常处理更加方便、但是存在异常传透的问题,就是链式调用中任何一个promise出现异常的时候,都会走到catch这一步。
5. 终极解决方式 async/awit
// 2.3 async/await: 回调地狱的终极解决方案
async function requset (){
try{
const result = await doSomething()
const newResult = await doSomethingElse()
const finalResult = await doThirdThing()
console.log('得到最终结果'+finalResult)
}catch(error){
failureCallback(error)
}
}
asynv/await 取消了回调函数 最后会自己编译
2.3 如何使用promise(语法)
2.3.1API
- Promise构造函数:Promise(excutor){} //执行器
excutor函数:同步执行(resolve,reject)=>{}
resolve函数:内部定义成功时我们调用的函数 value=>{}
reject函数:内部定义失败时我们调用的函数 reson=>{}
说明:excutor会在Promise内部立即同步回调,异步才做在执行器中执行 - promise.prototype,then方法:(onResolved,onRejected)=>{}
onResolved函数:成功的回调函数:value=>{}
onReject函数:失败的回调函数:reson=>{}
说明:指定用于得到成功value的成功回调和用于得到失败reson的失败回调,饭hi一个新的Promise对象 - Promise.prototype.catch方法:(onRejected)=>{}
onRejected函数:失败的回调函数(reson)=>{}
说明:then()的语法糖,相当于then(undefiend,inRejected) - Promise.resolve方法:(value)=>{}
value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象 - Promise.reject方法(reason)=>{}
reson:失败的原因
说明:返回一个失败的promise对象
举个栗子
//产生一个成功值为1的promise对象
const success1 = new Promise((resolve, reject) => {
resolve(1)
})
//产生一个成功值为2的promise对象 语法糖
const success2= Promise.resolve(2)
const error3=Promise.reject(3)
success1.then(value=>{
console.log(value) //打印结果为1
})
success2.then(value=>{
console.log(value) //打印结果为2
})
error3.catch(reason=>{
console.log(reason) //打印结果为3
})
error3.then(null,reason =>{
console.log(reason) //打印结果为3
})
- Promise.all()
const prALL= Promise.all([success1,success2,error3])
prALL.then(
value=>{
console.log("成功的值",value) //全部成功返回数组
},
reason=>{
console.log("失败的原因"+reason) //一个失败全失败
},
)
- Promise.race()
const pRace= Promise.race([success1,success2,error3])
pRace.then(
value=>{
console.log("谁先完成",value) //打印结果为1
},
reason=>{
console.log("失败的原因"+reason) //打印结果为3
})
},
)
2.3.2 Promise的几个关键问题
-
如何改变promise的状态?
(1)resolve(value):如果当前是pending会变为resolved
(2)reject(reson):当前如果是pedning就会变成rejectd
(3)抛出异常:如果当前是pending就会变成rejected -
一个promise指定多个成功失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
//出了resolve 和 reject 抛出之外 还可以通过js的throw 抛出
let p = new Promise(resolve,reject = >{
//resolve(1)
//reject(2)
thorw 3
})
p.then(
value=>{}
reason=>{
console.log("reason",reason) //打印结果为3
}
)
- 改变promise状态指定回调函数谁先谁后?
(1)都有可能,正常情况下是先指定回调再改变状态,但也见可以先改变状态再指定回调
(2)如何先改变状态再指定回调?{(1.再执行器中直接调用resolve()/rejected()2.延迟更长时间再调用then())
(3)什么时候才能得到数据?(1.如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据2.如果先改变状态,那当指定回调时,回调函数就会调用,得到数据)
//常规:先指定回调函数,后改变状态
new Promise((resolve,reject)=>{
setTimeout(()=>{ // 后改变状态(同时指定数据),异步执行回调函数
resolve(1)
},1000)
}).then(
value=>{console.log(value)},
// reason=>{console.log(reason)}
)
//如果先改变状态,再指定回调函数 如何写呢?
//1.最简单的就是去掉定时器 也就是去掉异步
new Promise((resolve,reject)=>{
resolve(1) //先改变的状态(同时指定数据)
}).then( //后指定回调函数,异步执行回调函数
value=>{},
reason=>{},
)
//2. 设置一个比异步更长房时间的定时器?
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{ // 后改变状态(同时指定数据),异步执行回调函数
resolve(1)
},1000)
})
//精妙?
setTimeout(()=>{
p.then(
value=>{console.log("value",value)},
reason=>{console.log("reason",reason)}
)
},1100)
//小tips 如何分别一个函数是同步还是异步 ? console 异步函数会在再队列里 先执行页面初始化代码函数 再执行异步队列里的函数
小例子
4. promise.then()返回的新的promise的结果由什么状态决定?
(1)简单表达:由then()指定的回调函数执行的结果决定
(1)详细表达:
a:如果抛出异常,新promise变为rejected。resaon为抛出的异常
b:如果返回的是非peomise的人一直,新promise变为resolved,value为返回值
c:如果返回的是另一个新的promise,此promise的结果就会变成新promise的结果
new Pormise((reslove,reject)=>{
resolve(1)
}).then(
value=>{
console.log(value) //打印结果为1
}
).then(
value=>{
console.log(value) //打印结果为undefined
}
)
哇哦 amazing~
5. promise如何串联多个操作任务?
(1)promise的then返回一个新的promise,可以写的then()的链式调用
(2)通过then的链式调用串联多个同步/异步任务
.promise的中断
//中断promise链
在一个promise.then(
return new Promise(
()=>{} //什么也不干
)
)