关于promise then的问题

新手上路,请多包涵
let p1=new Promise(resolve=>{
    resolve()
})
let p2=p1.then(data=>{
    return p2
}).then(res=>{
    console.log(res,'res')
})

为什么这样promise什么输出都没 也没有报错


let p1=new Promise(resolve=>{
    resolve()
})
let p2=p1.then(data=>{
    return p2
})

这样就会报错
Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

阅读 5k
2 个回答
let p2=p1.then(data=>{return p2;}).then(res=>{console.log(res,'res');})
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> pp1
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> pp2

then 会返回一个 promise ,这个 promise 使用它的回调的返回值来 resolve 的。

所以在上面,p1.then 返回的 pp1 ,是用 p2 resolve 的。
pp1.then 返回的 pp2 ,也就是 p2 ,是用回调返回的 undefined resolve 。

而,p2 是一个 promise 。所以,pp1 要等 p2 resolve 了它自己才能 resolve ,然后才能调用 pp1.then 的回调。
同时,p2 要等 pp1.then 的回调被调用并返回之后才能 resolve 。
于是互相等,谁也 resolve 不了。死锁。pp1.then 的回调不会被调用。

let p2=p1.then(data=>{return p2;})
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -> p2

这个就是用 p2 resolve p2 了。javascript 规定自己resolve自己是一个运行时错误 TypeError

使用有错误,promise返回自身会死循环。
一个promise对象只能是三种三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),并且不可逆,当标记成完成后,promise就结束了。
正确用法是。

let p1 = new Promise(resolve => {
    setTimeout(() => {
        resolve(Date.now())
    })
})
p1.then(data => {
    return data
}).then(data => {
    return data
}).then(data => {
    console.log('完成')
})

promise里不能返回promise本身,会造成无限循环。
至于promise返回自身后再追加then不报错,我猜测是因为promise实现代码中的逻辑是在执行promise的时候判断是否是Promise对象,如果是Promise就判断状态,状态时pending,就继续执行,如果有then、catch、finally方法就执行这些方法把promise的状态更改掉,循环就结束了。
但是如果没有thencatchfinally这些方法,那就可能会死循环,所以Promise的源码可能做了检查然后报出了这个错误提示,或者没有做检查,真的是运行时报的这个错。
我没有在V8的源码里找到相关的逻辑,下面是我的推测的伪代码,供参考

const PENDING = Symbol('PENDING');
const FULFILLED = Symbol('FULFILLED');
const REJECTED = Symbol('REJECTED');

…………

if (promise instanceof Promise) {
    // 判断状态
    if(this._status !== PENDING) {
        return;
    }
    try{
        // 标记状态
        promise.then(reject, resolve)
    } catch {
        // 标记状态
        …………
    } finally {
        …………
    }
}

then() {
    this.status = FULFILLED
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题