对象解构在 Promise 中解析和拒绝

Object destructuring resolve and reject in a Promise

对象解构不是我最喜欢的东西,我经常尽量避免使用它。但是在这种特殊情况下,我太好奇了,无法忽视正在发生的事情。

现在我们可以这样做了;

var o = {},
    x = 1,
    y = 2;
o = {x,y};
console.log(o); // <- {x: 1, y: 2}

一切都很好;

不过我的情况有点复杂;

var p = function(o){
          o.promise = new Promise((resolve,reject) => o = {resolve,reject});
          console.log(o)  // -< {resolve: ƒ, reject: ƒ}
          return o;
        }({});
console.log(p);           // -< {resolve: ƒ, reject: ƒ}

ppromise 属性 在哪里?所以我用经典的方式来做;

var q = function(o){
          o.promise = new Promise((resolve,reject) => ( o.resolve = resolve
                                                      , o.reject  = reject
                                                      ));
          console.log(o)  // <- {promise: Promise, resolve: ƒ, reject: ƒ}
          return o;
        }({});
console.log(q);           // <- {promise: Promise, resolve: ƒ, reject: ƒ}

我有一种奇怪的感觉,好像我错过了一些非常基本的东西,但我说不出来是什么。

您显示的任何代码都没有解构。

您正在将 o 重新分配给具有 2 个属性的简单普通新对象:

o = {resolve,reject}

这只是 ES2015 引入的 object literal shorthand syntax,与

相同
o = {resolve: resolve, reject: reject}

当然之前的 promise 属性 会丢失,因为它存在于一个不再被 o 引用的对象上(如果没有,最终将被垃圾收集内存中对它的其他引用)。

尝试

o.promise = new Promise((resolve,reject) => o = { ...o, resolve, reject });

不,...o 也不是解构,它是 object spread syntax

您也可以使用

o.promise = new Promise((resolve,reject) => o = Object.assign(o, { resolve, reject }));

请注意 Object.assign() 触发设置器(如果有),而扩展语法则不会。

问题

让我用这个方便的图表解释发生了什么:

var p = function(o){
//        1                2                          3
//        v           vvvvvvvvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
          o.promise = new Promise((resolve,reject) => o = {resolve,reject});
//                  ^                                   ^  ^^^^^^^^^^^^^^^
//                  6                                   5         4
          console.log(o)
          return o;
        }({});
console.log(p);
  1. o.promise的赋值开始。 引擎将首先引用 o,然后评估分配给 promise 属性 的内容。
  2. 赋值表达式是用new调用Promise构造函数。引擎必须先评估整个事物并获得一个对象,然后再将任何事物分配给 promise 属性.
  3. Promise 构造函数接收一个执行函数。该函数 立即被调用 。在执行者完成之前,promise 构造无法完成。
  4. 对于整个图片来说无关紧要,但为了清晰起见添加了 - 创建的对象具有两个属性 resolvereject
  5. 对象已分配给 o。请注意,第 1 步是作业的 start。任务还没有完成。这是中途。 代码 重新分配 oo.promise 的分配完成之前。然而,这发生在引擎引用了分配给 o 的初始对象之后。 现在有两个与 o 相关的对象:
    • 开始时第 1 步发生时分配的那个。
    • 分配给它的那个现在 - 在第 5 步之后。
  6. (为简洁起见,压缩内容):执行程序功能已完成。 Promise 构造函数也已完成。已计算完整表达式 new Promise((resolve,reject) => o = {resolve,reject}) 并生成一个值。 promise 属性 的赋值现在可以继续了。因为它使用来自步骤 1 的引用。代码分配给不再分配给 o.
  7. 的对象的承诺值
  8. (未显示)在步骤 1 中分配给 o 的初始对象。没有更多的引用。它不符合垃圾回收条件,将从内存中消失。 (初分配)o已死,万岁(新分配)o.

整个情况可以用这段代码简化和表示:

//get two references to the same object
let o1 = {};
let o2 = o1;

o1.promise = (() => o1 = {resolve: "foo", reject: "bar"})();
console.log(o1);
console.log(o2);

由于 Promise 构造函数只是调用执行函数,因此它被一个 IIFE 所取代,该 IIFE 在对其主体进行评估时表现相同。 o1o2 表示评估分配行的两个结果对象。由于 IIFE 重新分配了 o1o2 变量在那里向我们展示了初始对象发生了什么。它 确实 添加了 promise 属性 但(没有其他参考)它随后丢失了。

考虑到这一点,我们可以看到在原始代码中会发生类似的事情,如果我们有另一个对作为参数给出的对象的引用 o:

//have a separate reference for the object passed below
const foo = {};

var p = function(o){
  o.promise = new Promise((resolve,reject) => o = {resolve,reject});
  console.log("o is", o)
  return o;
}(foo);

console.log("p is", p);

//stack console shows `"promise": {}` but it's a promise object 
//check the browser console if you want to see it
console.log("foo is", foo);

console.log("foo.promise is a Promise:", foo.promise instanceof Promise);

解决方案

connexo shows that you can use Object.assign() in (收录于此以供参考):

var p = function(o){
          o.promise = new Promise((resolve,reject) => o = Object.assign(o, { resolve, reject }));
          console.log(o)
          return o;
        }({});
console.log(p);

//usage
p.promise
  .then(result => console.log(`Completed with ${result}`));
p.resolve(42);

之所以有效,是因为 o 没有重新分配,而是使用更多属性进行了增强。这也是带有逗号运算符的版本有效的原因 - 它仍然没有重新分配 o,只是修改它:

o.promise = new Promise((resolve,reject) => ( o.resolve = resolve
                                            , o.reject  = reject
                                            ));

另一种方法是对对象属性(而不是变量)使用解构赋值:

o.promise = new Promise((resolve,reject) => [o.resolve, o.reject] = [resolve, reject]);

或者如果你想要它更简洁:

o.promise = new Promise((...rs) => [o.resolve, o.reject] = rs);

var p = function(o){
          o.promise = new Promise((...rs) => [o.resolve, o.reject] = rs);
          console.log(o)
          return o;
        }({});
console.log(p);

//usage
p.promise
  .then(result => console.log(`Completed with ${result}`));
p.resolve(42);