5

I'm attempting to display the console.log output in a div. I'm doing this by overriding the console.log function

When I use the original console.log, a promise displays, in the console as (see 1. in code below):

Promise { <state>: "fulfilled", <value>: undefined }

When I over-ride it I get, in the div: :

 [object Promise]

(see 2. in code below)

How would you adjust the following code to have the <state> property displayed in the "main" div?

const pause =  sec => new Promise(r => setTimeout(r, 1000 * sec))

// 1. Original console.log
;(async function(){
  await new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve));
  let x = pause(0.5)
  console.log(x)
  await x;
  console.log(x)
})();

// 2. Overridden console.log
;(async function(){
  await new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve));
  await pause(1)
  let divconsole = document.getElementById('main');
  // Override console.log:
  console.log = function () {
    for (let i = 0; i < arguments.length; i++) {
      if (typeof arguments[i] === 'object') {
          divconsole.innerHTML += 'console.log arg ' + String(i) + ':  ' + String(arguments[i]) + '<br>';
      }
    }
  }

  let y = pause(0.5)
  console.log(y)
  await y;
  console.log(y)
})();
<div id="main" style="width:100%;height:auto;background-color:lightblue;">

</div>

A note on why I'm asking: I have code with a promise that resolves in a desktop browser and not in a mobile browser. I'm attempt to debug this (without consistent mobile-desktop OS ecosystem). I might write a follow up question if I can replicate it in a simple example.

Lee
  • 29,398
  • 28
  • 117
  • 170

2 Answers2

3

You can use this trick from this other SO answer to handle the arguments that are of type Promise like so. The trick requires the custom console.log to be asynchronous like so:

function promiseState(p) {
    const t = {};
    return Promise.race([p, t])
      .then(v =>
        (v === t) ? { state: "pending" } : { state: "fulfilled", value: v },
        () => { state: "rejected" }
      );
}

console.log = async function(...args) {
  for (let arg of args) {
    if (arg instanceof Promise) {
      let state = await promiseState(arg);
      divconsole.innerHTML += `Promise { &lt;state&gt;: "${ state.state }"${ state.state === "fulfilled" ? ', &lt;value&gt;: ' + state.value : '' } }<br>`;
    } else if (typeof arg === 'object') {
      divconsole.innerHTML += 'console.log arg:  ' + String(arg) + '<br>';
    }
    // add more else-ifs to handle strings, numbers, booleans, ... etc
  }
}

The custom console.log needs to be asynchronous because it wraps the promise in another promise. This is the only way you can do it.

Explanation:

The trick is to race the promise p against a resolved one (that yields an object {}), if the promise p is already fulfilled its value will be used and since its value can't be the same as that of the other promise (the one we are racing against, the one that yields {}), we determine that the promise has fulfilled. If the value we get is that of the other promise (the object {}) meaning that our resolved promise won the race, we can be sure that the promise p has not yet fulfilled.

If we get an error (meaning the catch callback gets called), the the promise p is rejected.

Demo:

function promiseState(p) {
  const t = {};
  return Promise.race([p, t])
    .then(v =>
      (v === t) ? { state: "pending" } : { state: "fulfilled", value: v },
      () => { state: "rejected" }
    );
}

console.log = async function(...args) {
  let divconsole = document.getElementById('main');
  
  for (let arg of args) {
    if (arg instanceof Promise) {
      let state = await promiseState(arg);
      divconsole.innerHTML += `Promise { &lt;state&gt;: "${ state.state }"${ state.state === "fulfilled" ? ', &lt;value&gt;: ' + state.value : '' } }<br>`;
    } else if (typeof arg === 'object') {
      divconsole.innerHTML += 'console.log arg:  ' + String(arg) + '<br>';
    }
  }
}

const pause =  sec => new Promise(r => setTimeout(r, 1000 * sec));

;(async function() {
  await new Promise(resolve => document.addEventListener('DOMContentLoaded', resolve));
  await pause(1);
  let divconsole = document.getElementById('main');

  let y = pause(0.5);
  await console.log(y);
  await y;
  await console.log(y);
})();
<div id="main" style="width:100%;height:auto;background-color:lightblue;"></div>
ibrahim mahrir
  • 31,174
  • 5
  • 48
  • 73
0

An easier way to cast anything promise-like to it's real value might be:

async function myLog(val) {
  console.log(await val);
}

await ensures that promises are resolved, but also passes through non-promises just fine.

Evert
  • 93,428
  • 18
  • 118
  • 189