在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: ƒ}

p..的promise属性在哪里。。?所以我用古典的方式来做;

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重新分配给一个简单的、具有两个属性的新对象:

o = {resolve,reject}

这只是ES2015引入的对象文字缩写语法,与相同

o = {resolve: resolve, reject: reject}

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

尝试

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

不,...o也没有破坏,它是对象扩展语法

你也可以使用

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

请注意,Object.assign()会触发setter(如果有的话(,而spread语法则不会。

问题

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

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. 赋值表达式是对具有newPromise构造函数的调用。引擎必须对整个事物进行评估并获得一个对象,然后才能将任何内容分配给promise属性
  3. Promise构造函数接收一个executor函数。该函数会立即被调用。在执行人完成之前,承诺的构建无法完成
  4. 不符合整个图片的顺序,但为了清晰起见,添加了一个具有两个属性resolvereject的对象
  5. 对象被指定给o。注意步骤1。是分配的开始。作业尚未完成。现在是中途代码在分配给o.promise之前重新分配o。但是,这种情况发生在引擎引用了分配给o的初始对象之后现在有两个对象与o相关:
    • 步骤1开始时分配给的。发生
    • 分配给它的现在-在步骤5之后
  6. (为简洁起见,压缩内容(:executor函数已完成。Promise构造函数也完成了。对完整表达式CCD_ 24进行了评估并产生了一个值。现在可以继续分配给promise属性。因为它使用了来自步骤1该代码分配给不再分配给o的对象的promise值
  7. (也没有图示(在步骤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构造函数只调用executor函数,因此它被IIFE所取代,IIFE在对其主体进行求值时表现相同。CCD_ 31和CCD_。由于IIFE重新分配了o1,因此o2变量会向我们显示初始对象发生了什么。它确实获得了一个添加到其中的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显示您可以在另一个答案中使用Object.assign()(此处包含以供参考(:

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);

相关内容

最新更新