一、promise是什么?
- promise是js中进行异步编程的新的解决方案(传统的方案是使用回调函数)
- promise是一个构造函数
- promise对象可以用来封装一个异步操作并且可以获取其成功/失败的结果值
异步操作有比如:数据库操作,定时器,Ajax,fs文件操作
二、为什么要使用promise?
支持链式调用,可以解决回调地狱问题
1.什么是回调地狱?
btn1.addEventListener("click",()=>{
btn2.addEventListener("click",()=>{
btn3.addEventListener("click",()=>{
console.log("hello world")
})
})
})
即:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件;
缺点:不便于阅读,不便于异常处理
解决方案:promise链式调用,指定回调函数的方式更加灵活
promise案例:
模拟异步操作回调:
需求:张三向多个女孩进行告白,但是向第二个女孩告白前,需要根据第一个女孩的回应,判断是否向第二个女孩告白,如果第一个女孩同意,则不向第二个女孩告白;我们将定义定时器和随机数模拟;当随机数小于0.3代表同意告白
传统的嵌套回调函数方式:
function sendLove(nvhai, callback) {
console.log(`张三向女孩${nvhai}告白了`)
setTimeout(() => {
if (Math.random() < 0.3) {
callback(true)
} else {
callback(false)
}
}, 1000)
}
sendLove('1', function(result) {
if (result) {
console.log("女孩1答应le,梅老师很高兴")
} else {
console.log("女孩1没答应")
sendLove('2', function(result) {
if (result) {
console.log("女孩2答应le,梅老师很高兴")
} else {
console.log("女孩2没答应")
sendLove('3', function(result) {
if (result) {
console.log("女孩3答应le,梅老师很高兴")
} else {
console.log("张三心灰意冷")
}
})
}
})
}
})
//张三向女孩1告白了
//05.html:28 女孩1没答应
//05.html:14 张三向女孩2告白了
//05.html:33 女孩2没答应
//05.html:14 张三向女孩3告白了
//05.html:36 女孩3答应le,张三很高兴
显而易见这样代码非常不利于阅读;
promise形式实现:
- 只要有一件异步事情发生时,就会存在两个阶段 unsettled未决阶段和settled已决阶段
- 事情总是从未决阶段走向已决阶段,并且未决阶段拥有控制通往已决阶段的能力,可以决定事情最终走向的结果
promise将程序分为三种状态 pending resolved rejected
- pending 等待 处于unsettled阶段,表示事情还在等待最终的结果
- resolved 已处理 处于setteled阶段,表示事情已经出现结果,并且可以按照正常的逻辑进行下去的结果
- rejected 已拒绝 处于setteled阶段,表示事情已经出现结果,并且不可以按照正常的逻辑进行下去的结果
- 把事情从pending状态推向resolved状态的过程中,可能会传递一些数据,这些数据为真实有效数据
- 把事情从pending状态推向rejected状态的过程中,可能会传递一些数据,这些数据为错误信息
- 无论是在哪个阶段还是那个状态,都是不可逆
当事情已经到达已决阶段后,通常用结果数据做一些后续处理,不同的已决结果,可能造成不同的后续处理
- resolved 后续处理表示为thenable
- rejected 后续处理表示为catchable
- 后续处理可能有多个,因此会形成任务队列,这些后续处理会按照顺序当到达对应的状态时会依次执行
用法:
Promise对象是一个构造函数,用来生成Promise对象
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
let pro = new Promise((resolve, reject) => {})
- resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”
- reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”
Promise实例方法:
- then()方法返回的是一个新的Promise实例,也就是promise能链式书写的原因
- catch()用于指定发生错误时的回调函数
- finally()用于指定不管 Promise 对象最后状态如何,都会执行的操作
function sendLove(nvhai) {
return new Promise((resolve, reject) => {
console.log(`张三向女孩${nvhai}告白了`)
setTimeout(() => {
if (Math.random() < 0.3) {
resolve(true) //同意
} else {
resolve(false) //不同意
}
}, 1000)
})
}
sendLove('1').then(res => {
if (res) {
console.log("女生1同意了")
return undefined
} else {
console.log("女生1没同意了")
return sendLove('2')
}
}).then(res => {//链式调用,根据上一次的结果,判断下一次如何执行
if (res === undefined) {
return
}
if (res) {
console.log("女生2同意了")
} else {
console.log("女生2没同意了")
return sendLove('3')
}
}).then(res => {
if (res === undefined) {
return
}
if (res) {
console.log("女生3同意了")
} else {
console.log("都被拒绝了")
}
});
注意点:
-
未决阶段的处理函数时同步的,会立即执行
-
已决阶段的状态只能有一个,resolve和reject不能同时使用,如果同时使用则只有第一个会执行
-
thenable和catchable函数是异步的,加入至微队列中
-
pro.then方法可以添加两个参数thenable和catchable,pro.catch方法可以是一个参数catchable
-
在未决阶段的处理函数中,如果发生未捕获的错误,会将状态推向rejected状态,并且会被catchable所捕获
-
如果前面的promise的后续处理,返回一个新的promise,则把新的promise的值和状态给then方法返回值。 如果前面的promise的后续处理,用return关键字进行返回,则then方法返回值的状态为resolve,值为返回值 用未捕获的错误进行返回,则then方法返回值的状态为reject,值为错误
promise静态方法
*resolve() | 静态方法 | 创建promise成功的实例 |
*reject() | 静态方法 | 创建promise失败的实例 |
*all(arr) | 静态方法 | 返回一个promise对象,当数组里面的所有promise对象的状态都为resolve, 则新的promise对象的状态为resolve,值为所有promise返回值的数组作为成功回调的返回值。反之有一个rejected状态,则新的promise对象的状态为rejected,把第一个触发失败的promise对象的错误信息作为它的失败错误信息 |
*race(arr) | 静态方法 | 方法返回一个 promise,一旦数组中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。 |
allSettled() | 静态方法 | 方法返回一个在所有给定的promise都已经resolve或rejected 后的promise,并带有一个对象数组,每个对象表示对应的promise结果。 |
any() | 静态方法 | 只要其中的一个 promise 成功,就返回那个已经成功的 promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise |
//all
let arr = [];
function sendLove(i) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
console.log(i, "失败")
reject(i)
} else {
console.log(i, "成功")
resolve(i)
}
}, Math.floor((Math.random() * 5000) + 1000))
})
}
for (let i = 0; i < 10; i++) {
arr.push(sendLove(i))
}
console.log(arr)
//race返回一个状态改变最快的promise
Promise.race(arr).then(data => {
console.log(data)
}, err => {
console.log(err)
})
三、async和await关键字
async 简化promise创建
充当函数修饰符,用于修饰函数声明或函数表达式,放在函数的开始位置,被修饰的函数一定会返回一个promise对象
await 相当于then方法的
await关键字必须放在被async修饰的函数里面,作用是相当于then方法中thenable
async和await关键字注意点
- 如果await关键字后面跟的不是一个promise对象,则直接输出await关键字后面的内容
- 如果async函数中有错误时,可以使用trycatch区捕获错误
async function test() {
// return 1
throw 2
}
test().then(data => {
console.log(data)
}, err => {
console.log(err)
});
(async function() {
try {
let a = await test()
console.log(a)
} catch (error) {
console.log(error)
}
})()