关于promise.then执行顺序,如何理解?

一身都是月~
  • 10
Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
})
回复
阅读 2.5k
5 个回答

这个和promise A+规范里,.then对传递函数结果的判定有关。。文字描述有点费力,贴一下之前按照promise A+实现的简易promise代码。

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

const resolvePromise = (promise,x,resolve,reject)=>{
  // 1. promise===x reject错误
  // 2. x是Promise函数, 将x的结果放入resolve和reject
  // 3. x是普通值,将x直接resolve
  if(promise === x){
    reject(TypeError('xxxxxx......'))
    return;
  }
  // 一般会判断x是对象,且x.then是方法,主要是为了应对和其他满足promise规范发Promise混用时的情况,此处简单点判断。。
  // if(x instanceof myPromise){
  if(typeof x?.then === 'function'){
    x.then((res)=>{ // 因为then函数return的是promise,因此x的状态需向上一层的promise传递
      resolve(res)
    },(rej)=>{
      reject(rej)
    })
    return;
  }
  resolve(x)
}
class myPromise {
  static resolve(val){
    return new myPromise(res=>{
      res(val)
    })
  }
  constructor(fn) {
    this.status = PENDING
    this.value = null
    this.reason = null
    this.resArr = []
    this.rejArr = []
    const resolve = (val) => {
      this.value = val
      this.status = FULFILLED
      this.resArr.forEach(func=>func())
    }
    const reject = (reason) => {
      this.reason = reason
      this.status = REJECTED
      this.rejArr.forEach(func=>func())
    }
    fn(resolve, reject)
  }
  then(onFulfilled,onReject) {
    const promise2 = new myPromise((res,rej)=>{
      if(this.status===FULFILLED){
        setTimeout(()=>{
          const x = onFulfilled(this.value)
          resolvePromise(promise2,x,res,rej)
        })
      }else if(this.status===PENDING){
        this.resArr.push(()=>{
          const x = onFulfilled(this.value)
          resolvePromise(promise2,x,res,rej)
        })
        this.rejArr.push(()=>{
          onReject(this.reason)
        })
      }
    })
    return promise2;
  }
  catch(fn) {
    this.rejArr.push(fn)
    this.status = REJECTED
  }
}

const test = ()=>{
  return new myPromise((res,rej)=>{
    setTimeout(()=>{
      res('step 1, ok');
    }, 1000)
  }).then(val=>{
    console.log(val);
    return new myPromise((res,rej)=>{
      setTimeout(()=>{
        res('step 2,ok')
      },1500)
    })
  }).then(val=>{
    console.log(val)
  })
}

const testFunc = async ()=>{
  const ts = new myPromise(res=>{setTimeout(()=>{res('100');console.log('ok')},2000)})
  const res = await ts
  console.log(res)
}

module.exports = myPromise;

先把代码里的setTimeout都当作微任务看,你return Promise.resolve(4)的操作实际走了.then里的第一个判断,.then返回的promise在reslove之前经过了两次setTimeout的等待

这是个 microtask 奇葩入队问题啊,并不涉及宏任务。

我的结论是:

下边这句代码会生成两个(没有log的)微任务
return Promise.resolve(4);

我的推理很简单

第一步,搞清楚去掉这句后,两个平行的promise链是如何入队的。

只有一个条promise链的情况下,是执行到then的时候才会入队。现在有两个,测试一下

Promise.resolve().then(() => {
    console.log(0);   
}).then(() => {
    console.log(2)
}).then(() => {
    console.log(4)
}).then(() => {
    console.log(6)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(7);
})

输出是 0 1 2 3 4 5 6 7

发现顺序是 上边入一个,下边入一个

image.png

拉直

image.png

我得出的结论是,两条平行promise链,是有序逐链跳跃入队的

所以加上这句后,也应该是跳跃入队的,

原题:

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4);
}).then((res) => {
    console.log(res)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
})

输出 0 1 2 3 4 5 6

经过测试,这句会被卡2次

image.png

说明这句插入了两个没有log的微任务

验证一下:给另一条链也加一句,抵消一下

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(2);
}).then((res) => {
    console.log(res)
}).then(() => {
    console.log(4);
})

Promise.resolve().then(() => {
    console.log(1);
    return Promise.resolve(3);
}).then((res) => {
    console.log(res)
}).then(() => {
    console.log(5);
})

猜结果?

image.png

答案 0 1 2 3 4 5

换个位置:

Promise.resolve().then(() => {
    console.log(0);
})
.then(() => {
    console.log(2)
}).then(() => {
    console.log(4)
}).then(() => {
    console.log(6)
}).then(() => {
    console.log(7)
}).then(() => {
    console.log(8)
}).then(() => {
    console.log(10)
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
    return Promise.resolve(9);
}).then(res =>{
   console.log(res)
})

答案 0 1 2 3 4 5 6 7 8 9 10

简单鲁个promise就明白了

const Promis = function(res){
    this.resolveArr = [];
    res && res(this.resolve.bind(this));
};
Promis.prototype.then = function(fn){
    this.resolveArr.push(fn);
    return this;
};
Promis.prototype.resolve = function(){
    setTimeout(()=>{
        this.resolveArr.forEach(fn => {
            fn && fn()
        })
    },0)
}

new Promis(res=>{
console.log(1)
res()
}).then(()=>{console.log(2)}).then(()=>{console.log(3)})

image.png

其实是先执行的then,把回调都push到数组里,resolve的时候一个一个调。

你知道吗?

宣传栏