Promise是异步问题同步化解决方法
三种状态
- pending - 未知
- resolved - 成功
- rejected - 失败
变换过程
pending > resolved
pending > rejected
只有这两种,且promise 对象只能改变一次,无论成功还是失败,都会有一个结果。
案例
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
const date = Date.now();
if(date % 2 === 0){
resolve('成功',date)
}else{
reject('失败',date)
}
})
})
// then里面可以写成功和失败两种回调
p.then(value =>{
console.log(value,' - 成功的回调函数')
},reason=>{
console.log(reason,' - 失败的回调函数')
})
语法糖
const p1 = new Promise((resolve,reject)=>{
resolve(1)
})
const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);
p1.then(value => console.log(value)) // 1
p2.then(value => console.log(value)) // 2
p3.then(null,value => console.log(value)) // 3
p3.then(reason => console.log(reason)) // 3
All
Promise.all()
所有异步全部返回成功时候才会执行。其中有一个返回失败都不会执行。返回值是以数组类型返回!
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const pAll = Promise.all([p1,p2]);
pAll.then(values =>{
console.log(values); // [1,2]
}).catch(reason => {
console.log(reason);
})
race
Promist.race()
根据返回速度最快
的结果来执行,从左往右执行,速度最快的返回成功Promise.race()
返回成功,速度最快的返回失败Promise.race()
返回失败!
const p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject();
})
});
const p2 = Promise.resolve(2);
const pRace = Promise.race([p1,p2]);
pRace.then(values =>{
console.log(values); // 2
}).catch(reason => {
console.log(reason);
})
错误抛出
const p = new Promise((resolve, reject) =>{
// pending状态转其他状态只能转一次,所以下面只能执行一个。需要注释其他的!
// resolve(1); // 成功
// reject(2); // 失败
// throw new Error(3); // 抛出错误会由pending > rejected(失败状态)!
throw 4; // 可以直接抛出任意值;
} )
p.then(value => console.log(value))
.catch(reason => console.log(reason))
执行顺序
异步参与后,会先执行回调函数 > 保存起来,后改变状态; 常规:先执行回调函数,后改变状态;
// 先执行then后执行Promise。先执行回调再改变状态;
new Promise(resolve => {
setTimeout(()=>{
resolve(1)
})
}).then(value => {
console.log(value);
})
// 先执行Promise后执行then,先改变状态,后执行then
new Promise((resolve => {
resolve(2)
})).then(value => {
console.log(value)
})
难点
new Promise(resolve => {
resolve(1);
}).then(
value => console.log(value), // 返回1
).then(
value => console.log(value), // undefined
)
为什么会出现这种情况呢?因为第一个then返回的就是一个undefined
。所以第二个then收到的值是undefined
;如果第二个then需要接受一个成功的值,第一个then必须返回一个Promise对象;如下
第一个then的结果会作为`Promise`返回的结果返回给下一个then。
new Promise(resolve => {
resolve(1);
}).then(
value => {
console.log(value)
return 1 // resolve 1
return Promise.resolve(2); // resolve 2
return Promise.reject(3); // reject 3
throw 4 // reject 4
}
).then(
value => console.log(value),
reason => console.log(reason)
)
串联多任务
同步任务返回可以直接return,如果是异步任务需要重新
new Promise
- Promise的then()返回一个新的Proimise,可以看成then()的链式调用
- 通过then的链式调用串联多个同步/异步任务
new Promise(((resolve, reject) => {
setTimeout(()=>{
console.log('异步任务1')
resolve('resolve - 异步任务1')
},100)
})).then(value => {
console.log(value);
return '同步任务2'
}).then(value => {
console.log('同步任务2')
console.log('resolve - ' + value)
return new Promise ((resolve, reject) => {
setTimeout(()=>{
resolve('异步任务3');
})
})
}).then(value => {
console.log(value)
console.log('resolve - 异步任务3')
})
异步任务1和异步任务2返回的都是第一个Promise,再遇到异步任务需要重新new Promise
并且返回,给下面then使用!
异常穿透
遇到错误会一层层往下寻找,
注释部分为系统内置寻找的方法
。直到遇到catch
并且抛出错误信息!
new Promise((resolve, reject) => {
reject('错误抛出');
}).then(value => console.log(value),
// reason => {throw reason}
).then(value => console.log(value),
// reason => {throw reason}
).catch(reason => {
console.log(reason)
})
中断Promise链接
new Promise((resolve, reject) => {
reject('错误抛出');
}).then(value => {
console.log(value)
}).catch(reason => {
console.log(reason)
// 返回一个pending的promise > 所以会中断下面的语法,什么都会执行!
return new Promise(() => {})
}).then(
value => {
console.log(value)
}, reason => {
console.log(reason)
})
async-await
async
async
函数内部处理的时候,会套一层Promise
,async
函数返回的Promise
的结果由函数执行的结果决定,把函数的返回值用Promise返回;成功返回resolve
,失败返回reject
。
async function fun(){
// 失败
// return throw 2;
// return Promise.reject('失败');
// 成功
// return 1;
return new Promise(((resolve, reject) => {
setTimeout(()=>{
resolve('成功')
},1000)
}))
}
const result = fun()
// 返回一个Promise对象
console.log(result)
result.then(value => {
console.log(value)
}).catch(reason => {
console.log(reason)
})
await
await
操作符用于等待一个Promise
对象,只能在异步函数async function
种使用,如果返回是一个Promise
,返回Promise
对象的处理结果,如果等待的不是Promise
对象,则返回该值本身。
function fun1(){
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('成功')
},2000)
})
}
function fun2(){
return 2
}
async function fun3() {
// await右侧表达式为Promise,结果就是Promise的value
const result1 = await fun1();
// await右侧不是Promise,结果就是它自己
const result2 = await fun2()
// await处理完后的结果是同步执行
// result1等待2秒后才能执行result2
console.log(result1)
console.log(result2)
}
fun3()
await遇到错误
await
的promise
失败了。就会抛出异常,需要通过try{}catch(e){}
来捕获错误
function fun4(){
return new Promise((resolve, reject) => {
setTimeout(()=>{
reject('失败')
},1000)
})
}
async function fun5() {
try{
const result = await fun4();
console.log(result)
}catch(err){
console.log('catch - ',err)
}
}
fun5()
async-await
async await
是解决回调地狱的最优方案~
async function fun() {
try{
const a = await fn1();
const b = await fn2(a);
const c = await fn3(b);
console.log(c)
}catch (e) {
console.log(e)
}
}
js异步之宏队列与微队列
示意图
原理
JavaScript是单线程执行,运行过程中如果先遇到宏任务或者微任务,系统会先挂起来先执行同步任务,同步任务结束后依次执行。执行循序
同步任务 > 微任务 > 宏任务
。 宏任务里面存在微任务的执行 > 同步任务 > 宏任务里面的微任务 > 在运行中的剩余宏任务 > 跳出运行完毕的宏任务继续执行下一个宏任务 > 依次循环
面试题
习题1
setTimeout(()=>{
console.log(1);
})
new Promise(resolve => {
console.log(2);
resolve();
}).then(value => {
console.log(3)
}).then(value => {
console.log(4)
})
console.log(5)
/*
宏[1]
微[3,4]
同[2,5]
*/
// 2 5 3 4 1
习题2
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)
/*
宏[5]
微[1,2]
同[3,7,4]
*/
习题3
setTimeout(()=>{
console.log(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)=>{
console.log(7);
resolve();
}).then(()=>{
console.log(8)
})
/*
宏[0]
微[2,3,8,4,6,5]
同[1,7]
我刚开始开的时候有个疑惑为什么不是4执行完后执行5?
[4,6] 4的微任务执行完后才能把5放到微任务里面吧;所以在6后面执行;
*/