本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
1.Promise介绍
promise是js的重点也是难点,无论是日常开发还是面试都需要我们去熟练掌握它。下面我将根据个人理解进行Promise的讲解。
2.Promise的状态
- 默认状态(pending)
- 成功状态(fulfilled)----resolve触发
- 失败状态(fulfilled)----reject或者throw error触发
promise创建成功后,默认是pending状态。它具备原子性,状态只能从默认状态转换到成功或者失败的一种。
栗子1
let p = new Promise((resolve,reject)=>{
resolve("1")
}).then(data=>{
console.log("成功",data)
}).catch(err=>{
console.log("失败",err)
})
输出成功1,promise状态从默认到成功
栗子2
let p = new Promise((resolve,reject)=>{
reject("1")
}).then(data=>{
console.log("成功",data)
}).catch(err=>{
console.log("失败",err)
})
输出失败1,状态由默认修改为reject
栗子3
let p = new Promise((resolve,reject)=>{
throw new Error("2")
}).then(data=>{
console.log("成功",data)
}).catch(err=>{
console.log("失败",err)
})
输出失败2,throw也可以触发catch
3.then,catch的细节特性
-
then,catch它们都是默认向下传递promise
-
then,catch如果没有返回值,除了抛出一个错误会向下传递一个失败的Promise,其它所有情况都会返回一个Promise.reoslve(unefined),也就是下一个永远执行一个成功的Promise
-
then,catch如果有返回值,除了返回Promise.reject执行的是一个失败的Promise,其它情况的都是返回成功的Promise。
栗子4
let p = new Promise((resolve,reject)=>{
resolve(1)
}).then(data=>{
console.log("成功1",data)
}).then(data=>{
console.log("成功2",data)
}).catch(err=>{
console.log("失败",err)
}).then(data=>{
console.log("成功3",data)
})
输出结果
首先由于是resolve,将值1传递到了第一个成功回调输出成功1。继续向下传递,没有返回值则默认向下传递undefined的成功Promise,输出了成功2 undefined,同理,来到最后一个成功3 undefined
栗子5
let p = new Promise((resolve,reject)=>{
reject(1)
}).then(data=>{
console.log("成功1",data)
}).then(data=>{
console.log("成功2",data)
}).catch(err=>{
console.log("失败1",err)
}).then(data=>{
console.log("成功3",data)
}).catch(err=>{
console.log("失败2",err)
})
输出结果
根据上面的特性很好分析,首先是失败的Promise会被第一个catch拦截,输出了失败1。接着根据上面特性catch和then都会默认向下继续传递一个成功的Promise,因此输出了成功3
栗子6
let p = new Promise((resolve,reject)=>{
resolve(1)
}).then(data=>{
throw new Error(data)
}).then(data=>{
console.log("成功2",data)
}).catch(err=>{
console.log("失败1",err)
}).then(data=>{
console.log("成功3",data)
}).catch(err=>{
console.log("失败2",err)
})
输出结果
首先执行的resolve,来到了第一个then,仔细看,虽然没有返回值,但是这里是抛出了一个错误,相当于向下传递一个错误的Promise。因此来到了失败1。继续没有返回值,传递一个undefined的成功promise,所以来到了成功3
栗子7
let p = new Promise((resolve,reject)=>{
resolve(1)
}).then(data=>{
return new Error(data)
}).then(data=>{
console.log("成功2",data)
}).catch(err=>{
console.log("失败1",err)
}).then(data=>{
console.log("成功3",data)
}).catch(err=>{
console.log("失败2",err)
})
输出结果
首先是执行一个成功的promise,来到了第一个then,这里是返回一个错误对象,看清楚,只是返回一个错误对象,不是抛出错误,也不是返回一个promise.reject。因此它还是向下传递一个成功的promise对象。来到了成功2,成功2里面没有返回值,继续传递来到了成功3
栗子8
let p = new Promise((resolve,reject)=>{
resolve(1)
}).then(data=>{
return Promise.reject(2)
}).then(data=>{
console.log("成功2",data)
}).catch(err=>{
console.log("失败1",err)
}).then(data=>{
console.log("成功3",data)
}).catch(err=>{
console.log("失败2",err)
})
输出结果
这个很好判断,根据上面的规则,有返回值并且是promise.reject因此传递是一个错误的promise.所以来到了失败1,然后失败1无返回值,传递一个默认的正确的promise,执行成功3
4.值穿透现象
promise的then和catch传递的不是函数时,会发生值传统现象,意思就是如果then和catch里面传递的是一个非函数,则向下传递的值是无效的。
- 当promise发生了值传统,我们可以忽略其存在,然后判断向下传递的值。
栗子9
let p = new Promise((resolve,reject)=>{
resolve(1)
}).then(2).then(data=>{
console.log(data)
})
输出1,这里就是值穿透现象,第一个then传递是一个非函数,因此我们可以忽略它的存在,直接看传递是函数的then,输出1。
5.await后面是微任务
async await语法糖中,await后面代码是进入微任务队列执行,await修饰的代码还是同步的
async function runAsync() {
console.log("runAsync start");
await asyncFunc();
console.log("runAsync end");
}
async function asyncFunc() {
console.log("do something");
}
runAsync();
console.log('start')
/*
runAsync start
do something
start
runAsync end
*/
总结
- then,catch无返回值时,默认会自动向下传递promise的成功状态,携带参数是undefined。唯一特殊的是then和catch里面抛出一个错误,此时会向下传递一个失败prmise状态
- then,catch有返回值是,默认向下传递一个成功的promise,携带参数就是这个返回值。唯一特殊的是返回一个promise.reject,此时向下传递的就是一个失败的promise状态
- then和catch传递的参数是非函数时,会发生值穿透现象,只需要忽略传递非函数的过程,剩余部分按照规则1和2继续执行就可以。