Promise是异步问题同步化解决方法

三种状态

  • pending - 未知
  • resolved - 成功
  • rejected - 失败

变换过程

pending > resolved

pending > rejected

只有这两种,且promise 对象只能改变一次,无论成功还是失败,都会有一个结果。

案例

  1. const p = new Promise((resolve,reject)=>{
  2. setTimeout(()=>{
  3. const date = Date.now();
  4. if(date % 2 === 0){
  5. resolve('成功',date)
  6. }else{
  7. reject('失败',date)
  8. }
  9. })
  10. })
  11. // then里面可以写成功和失败两种回调
  12. p.then(value =>{
  13. console.log(value,' - 成功的回调函数')
  14. },reason=>{
  15. console.log(reason,' - 失败的回调函数')
  16. })

语法糖

  1. const p1 = new Promise((resolve,reject)=>{
  2. resolve(1)
  3. })
  4. const p2 = Promise.resolve(2);
  5. const p3 = Promise.reject(3);
  6. p1.then(value => console.log(value)) // 1
  7. p2.then(value => console.log(value)) // 2
  8. p3.then(null,value => console.log(value)) // 3
  9. p3.then(reason => console.log(reason)) // 3

All

Promise.all()所有异步全部返回成功时候才会执行。其中有一个返回失败都不会执行。返回值是以数组类型返回!

  1. const p1 = Promise.resolve(1);
  2. const p2 = Promise.resolve(2);
  3. const pAll = Promise.all([p1,p2]);
  4. pAll.then(values =>{
  5. console.log(values); // [1,2]
  6. }).catch(reason => {
  7. console.log(reason);
  8. })

race

Promist.race()根据返回速度最快的结果来执行,从左往右执行,速度最快的返回成功Promise.race()返回成功,速度最快的返回失败Promise.race()返回失败!

  1. const p1 = new Promise((resolve,reject)=>{
  2. setTimeout(()=>{
  3. reject();
  4. })
  5. });
  6. const p2 = Promise.resolve(2);
  7. const pRace = Promise.race([p1,p2]);
  8. pRace.then(values =>{
  9. console.log(values); // 2
  10. }).catch(reason => {
  11. console.log(reason);
  12. })

错误抛出

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函数内部处理的时候,会套一层Promiseasync函数返回的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遇到错误

awaitpromise失败了。就会抛出异常,需要通过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异步之宏队列与微队列

示意图

Promise - 图1

原理

JavaScript是单线程执行,运行过程中如果先遇到宏任务或者微任务,系统会先挂起来先执行同步任务,同步任务结束后依次执行。执行循序同步任务 > 微任务 > 宏任务。 宏任务里面存在微任务的执行 > 同步任务 > 宏任务里面的微任务 > 在运行中的剩余宏任务 > 跳出运行完毕的宏任务继续执行下一个宏任务 > 依次循环

Promise - 图2

面试题

习题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后面执行;
 */