Problem
Let me explain what is happening using this handy diagram:
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);
- The assignment to o.promisestarts. The engine will take a reference toofirst, then evaluate what to assign to thepromiseproperty.
- The assignment expression is the call to the Promiseconstructor withnew. The engine has to evaluate the entire thing and get an object before it assigns anything to thepromiseproperty.
- The Promiseconstructor receives an executor function. That function is immediately invoked. The promise construction cannot finish until the executor finishes.
- Inconsequential for the whole picture but added for clarity - an object is created with two properties resolveandreject.
- The object is assigned to o. Note that step 1. is the start of the assignment. The assignment has not finished. It's midway. The code reassignsobefore the assignment too.promisecompletes. However, this happens after the engine has taken a reference to the initial object assigned too. There are now two objects related too:
- The one which was assigned at the start when step 1. takes place.
- The one that is assigned to it now - after step 5.
 
- (compacting things for brevity): the executor function completed. The Promiseconstructor also completed. The full expressionnew Promise((resolve,reject) => o = {resolve,reject})has been evaluated and produced a value. The assignment to thepromiseproperty can now continue. Since it uses the reference from step 1. the code assigns to the promise value of an object that is no longer assigned too.
- (nor pictured) the initial object that was assigned to oat step 1. has no more references to it. It is not eligible for garbage collection and will disappear from the memory. (the initially assigned)ois dead, long live (the newly assigned)o.
The whole situation can be simplified and represented by this code:
//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);
 
 
Since the Promise constructor just invokes the executor function, it's replaced with an IIFE that behaves the same in regards to when evaluation of its body takes place. o1 and o2 represent the two resulting objects from evaluating the assignment line. Since the IIFE reassigns o1, the o2 variable is there to show us what happened to the initial object. It does get a promise property added to it but (without another reference) it's then lost.
With this in mind, we can see that a similar thing would happen in the original code, if we have another reference to the object which is given as an argument for 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);
 
 
Solution
connexo shows that you can use Object.assign() in another answer (included here for reference):
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);
 
 
This works because o is not reassigned but enhanced with more properties. This is also why the version with the comma operator works - it still doesn't reassign o, just modifies it:
o.promise = new Promise((resolve,reject) => ( o.resolve = resolve
                                            , o.reject  = reject
                                            ));
Another alternative is to use destructuring assignment onto object properties (as opposed to variables):
o.promise = new Promise((resolve,reject) => [o.resolve, o.reject] = [resolve, reject]);
Or if you want it more concise:
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);