为什么要有Promise
因为回调地狱,多层嵌套的callback会让基本超过五层嵌套的代码难以理解,并且难以维护和重构,所以出现了Promise
Promise
Promise是一个构造函数,接受一个executor函数作为参数,executor函数有两个函数形参resolve,reject,返回一个Promise对象,异步处理成功调用resolve函数,失败则调用reject函数
js
let p = new Promise( function(resolve, reject) {
//异步处理
//然后执行resolve或者reject
} )
resolve和reject就像两个预案,成功了就执行resolve方案,失败了就执行reject方案
一个 Promise有以下几种状态:
pending: 初始状态,既不是成功,也不是失败状态。
fulfilled: 意味着操作成功完成。
rejected: 意味着操作失败。
pending 状态的 Promise 对象可能触发fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发失败状态(rejected)并传递失败信息。
当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。
当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。
- 如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。
- 如果then中的回调函数抛出一个错误,那么then返回的Promise将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。
- 如果then中的回调函数返回一个已经是接受状态的Promise,那么then返回的Promise也会成为接受状态,并且将那个Promise的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。
- 如果then中的回调函数返回一个已经是拒绝状态的Promise,那么then返回的Promise也会成为拒绝状态,并且将那个Promise的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。
- 如果then中的回调函数返回一个未定状态(pending)的Promise,那么then返回Promise的状态也是未定的,并且它的终态与那个Promise的终态相同;同时,它变为终态时调用的回调函数参数与那个Promise变为终态时的回调函数的参数是相同的。
resolve(成功) onFulfilled会被调用
js
let promise = new Promise((resolve, reject) => {
resolve('fulfilled') // 状态由 pending => fulfilled
});
promise.then(result => { // onFulfilled
console.log(result) // 'fulfilled'
}, reason => {
// onRejected 不会被调用
})
reject(失败) onRejected会被调用
js
let promise = new Promise((resolve, reject) => {
reject('rejected') // 状态由 pending => rejected
})
promise.then(result => { // onFulfilled 不会被调用
}, reason => { // onRejected
console.log(reason) // 'rejected'
})
then的执行逻辑
js
var aaa = new Promise((resolve, xxx) => {
xxx("第一次失败了")
})
aaa.then(
() => {
console.log("成功了")
},
(error) => {
console.log(error) //第一次失败了
return Promise.reject('第二次又失败了')
}
).then(()=>{
},(error)=>{
console.log(error) //第二次又失败了
})
只有当aaa的状态变成rejected也就是aaa里面明确写了reject那么才会返回错误,如果你写的是resolve那么则会返回成功了
promise的API我就不介绍了,基本常用的也就是then,遇到不懂的直接面向mdn编程就好
如果你想彻底消除回调函数
js
p.then(onFulfilled, onRejected).
then((success)=>{console.log('成功')},(error)=>{console.log('失败')}).then(...,...)....
then方法里面还是有两个回调函数,如果你看它不爽怎么办
await
await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。把异步代码变成同步代码
js
function 等两秒() {
return new Promise(resolve => {
setTimeout(() => {
console.log('我已经等了2s了')
}, 2000)
})
}
//异步写法
等两秒().then(res=>{
})
//同步写法
let success = await 等两秒()
console.log(sucess) //我已经等了2s了
await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。
若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。
如果你想拿到这个错误
js
try{
.....
}catch(error){
console.log(error)
}
另外,如果 await 操作符后的表达式的值不是一个 Promise,那么该值将被转换为一个已正常处理的 Promise。
async
async function foo() {
return 'hello async'
}
foo()
//Promise {<resolved>: "hello async"}
当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。
用不用async和await有什么区别?
先看看普通调用
function foo() {
return new Promise(resolve => {
setTimeout(() => resolve('这是一个异步操作'), 1000)
})
}
foo().then(res => {
console.log(res)
})
使用async和await
function foo() {
return new Promise(resolve => {
setTimeout(() => resolve('这是一个异步操作'), 1000)
})
}
async function test() {
let res = await foo()
console.log(res)
}
test()
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。