RSVP Promises疑问:父Promise为何等待非链式内部Promise?
嘿,我来帮你搞清楚这个RSVP.js的问题~
先把你的代码贴出来方便分析:
new RSVP.Promise(function (resolve) { console.log('1'); var p = new RSVP.Promise(function (innerResolve) { setTimeout(function () { console.log('3'); innerResolve(); }, 5000); }); resolve(p); }).then(function () { console.log('2'); });
你的预期和实际行为的差异
你原本以为会先输出1,紧接着输出2,5秒后再输出3——觉得内部的Promise p会被当作普通值直接处理,外部Promise会立即resolve触发then回调。但实际运行下来,应该是先输出1,然后等5秒后输出3,最后才是2,对吧?
问题出在哪:Promise同化的规则
这里的核心是Promise同化这个机制——简单说就是:当你用一个Promise对象去resolve另一个Promise时,规范要求外部Promise必须等内部的Promise完成(状态变成resolved或者rejected),才会继续执行后续的then或者catch回调。
你误以为旧版RSVP.js不支持这个规则,会把p当成普通对象直接返回,但实际上RSVP.js很早就实现了这个行为(除非是非常非常早期的版本)。所以当你调用resolve(p)时,外部Promise并没有立刻完成,而是“挂起”等待p的状态变化,直到5秒后innerResolve()执行,p变成resolved,外部Promise才会触发自己的then回调,输出2。
怎么得到你想要的输出顺序
如果你确实需要让外部Promise立即resolve,把p当作普通值处理,最简单的办法是把p包装成一个非Promise的结构,比如数组或者对象:
new RSVP.Promise(function (resolve) { console.log('1'); var p = new RSVP.Promise(function (innerResolve) { setTimeout(function () { console.log('3'); innerResolve(); }, 5000); }); // 用数组把p包起来,避免被RSVP识别为Promise进行同化 resolve([p]); }).then(function (result) { console.log('2'); // 要是之后需要用到p,直接取result[0]就行 });
这样外部Promise会立刻完成,then回调马上执行输出2,5秒后内部Promise的定时器触发,输出3,完全符合你的预期。
当然,如果只是单纯想调整输出顺序,也可以把console.log('2')放到内部Promise的回调里,但估计这不是你想要的实现方式~
最后总结一下
多数旧版本的RSVP.js已经遵循了Promise规范里的同化规则,resolve一个Promise时会自动等待它完成。如果要绕过这个机制,只要把目标Promise包装成普通值(比如数组、对象),不让RSVP识别出来就行。
内容的提问来源于stack exchange,提问作者nogridbag

