promise常见问题总结

promise常见问题总结

promise如何中断

promise的缺点之一就是无法让promise中断

Promise.resolve().then(() => {
    console.log('then 1')
}).then(() => {
    console.log('then 2')
}).then(() => {
    console.log('then 3')
}).catch((err) => {
    console.log(err)
})

如上代码如何让promise的链式调用中断?

  • 一种方法是在then 1中直接抛错, 这样就不会执行then 2, then 3, 直接跳到catch方法打印err(但此方法并没有实际中断)
Promise.resolve().then(() => {
    console.log('then 1')
    throw new Error('xxx')
}).then(() => {
    console.log('then 2')
}).then(() => {
    console.log('then 3')
}).catch((err) => {
    console.log(err)
})
  • 另一种方法就是在then 1中return 一个新的Promise,但不改变其状态,这样该Promise就一直处于pedding状态,即不会执行后面任何方法
Promise.resolve().then(() => {
    console.log('then 1')
    return new Promise(() => {})
}).then(() => {
    console.log('then 2')
}).then(() => {
    console.log('then 3')
}).catch((err) => {
    console.log(err)
})
  • 中断有啥用? ---------- 可以让我想到超时中断问题

假设我们要用promsie自己封装一个ajax, 设置超时中断时间,如果没返回就不用返回了, 返回也不做处理了,不重要(虽然和上面的原理没关系,但不重要,上面是一个promsie的链式调用中断,此例是实际应用中的问题,你管我用几个promsie呢, 想到了记录一下)

function wrap(p1) {
    let abort
    let p2 = new Promise((resolve, reject) => {
        abort = reject
    })
    let p = Promise.race([p1, p2])
    // 将延时promise的reject方法挂在到p1与p2的race后的promise上, 可以在方法外通过调用p的cancel方法,来触发p2的reject
    p.cancel = abort
    return p
}

let fn = wrap(new Promise((resolve, reject) => {
    // 假设1秒后ajax返回, 调用resolve
    setTimeout(() => {
        resolve()
    }, 1000)
}))

fn.then(() => {
    console.log('ok')
}).catch(() => {
    console.log('err')
})

// 设置延时时间500ms, 如果500ms数据买回来,就中断
setTimeout(() => {
    fn.cancel()
}, 500)

promise 微任务的执行顺序

之前的promise中断好歹还可以强说有点用,下面这种例子,谁要是没事写在生产代码中,直接开除好吧~~~寻思寻思得了

const p = Promise.resolve();
;(()=>{
    const implicit_promise = new Promise(resolve =>{
        const promise = new Promise(resolve=>{
            resolve(p)
        }); 
        promise.then(()=>{ 
            console.log('after:await');
            resolve()
        })
    });
    return implicit_promise
})();
p.then(()=>{
    console.log('tick:a');
}).then(()=>{
    console.log('tick:b');
}).then(()=>{
    console.log('tick:c');
});

首先第一行生成一个resolve成功态的promise,然后自执行函数同步代码直接执行,
第5行resolve(p), 这里要知道resolve一个promsie要等到该promise执行then方法后才能拿到返回值
也就是说第7行的then要等到p的返回值拿到之后才执行
下面先把下面的链式编程拆一下

const p = Promise.resolve();
;(()=>{
    const implicit_promise = new Promise(resolve =>{
        const promise = new Promise(resolve=>{
            resolve(p)
        });
        promise.then(()=>{ 
            console.log('after:await');
            resolve()
        })
    });
    return implicit_promise
})();
let p1 = p.then(()=>{
    console.log('tick:a');
})
p1.then(()=>{
    console.log('tick:b');
}).then(()=>{
    console.log('tick:c');
});

自执行函数执行完,执行p1的回调先打印tick:a,且由于没有return值,所以默认return一个新的promise也就是p1接收的心promise
然后紧接着p1调用then方法,之后第7行也调用then方法, 所以相继打印tick:b和after:await
等到tick:b执行完返回一个新的promise,这才执行tick:c
所以打印顺序为 tick:a ==> tick:b ==> after:await ==> tick:c
这就是面向面试学习的精髓

async+await是怎么转化为promise的?

async function async1(){
    console.log('async1 start')
    await async2();
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout') 
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end');

这段代码在很多地方看到了,其他执行顺序很容易理解,唯一的问题就在于async1 end什么时候执行,我们先不考虑这行console
就很容易可以得到以下结果

// script start (同步代码,上面是两个函数还没有调用)
// async1 start (调用async1的时候执行async1函数,打印async1 start)
// async2 (在async1中调用async2, 打印async2)
// promsie1 (同步代码执行到new Promise, 在executor中执行同步代码, 打印promise1)
// script end (promise1 打印后暂不执行promsie2因为then回调是在微任务中,先执行同步代码, 打印script end)
// promise2 (同步代码执行完, 清空微任务)
// setTimeout (宏任务执行)

那么问题就在于async1 end什么时候执行

毫无疑问现在的问题在于await的原理,他肯定也是异步的微任务, 问题在于async1 end和promise2 谁先执行,
首先在node环境下测试是promise2先执行,但是在chrome中执行是async1 end先执行
由此可以得出await转化promise
在node中

async2().then(() => {
    console.log('async1 end')
})
// 你以为是这么转化的?我也是这么以为的,**但是**在node中的then函数中它默认用Promise又包裹一层所以是这样的
async2().then(() => {
    return Promise.resolve().then(() => {
        console.log('async1 end')
    })
})
// 有点恶心了~~~我测试的版本是v8.6.0以上版本
// 这样的执行顺序就是async1 end 在promise2之后
// script start
// async1 start
// async2
// promise1
// script end
// promise2
// async1 end
// setTimeout

但在chrome(74.0.3729.169)中的转化应该是只有一个then,then中直接调用console,所以在浏览器中就是

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

这个转化过程是通过结果推出来的,总之开发环境还是要避免这样的代码出现