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
这个转化过程是通过结果推出来的,总之开发环境还是要避免这样的代码出现