16

In the OpenUI5 code-base I came across this snippet:

// Wait until everything is rendered (parent height!) before reading/updating sizes.
// Use a promise to make sure
// to be executed before timeouts may be executed.
Promise.resolve().then(this._updateTableSizes.bind(this, true));

It looks like the native Promise function is being used, with no argument being passed to it's resolve function which takes an:

Argument to be resolved by this Promise. Can also be a Promise or a thenable to resolve.

So, since it looks like the promise would simply immediately resolve and invoke then's callback, perhaps the intent is similar to:

var self = this;
setTimeout(function() {
    self._updateTableSizes.bind(self, true)
}, 0);

...basically, freeing the JavaScript run-time event-loop to finish other things (like rendering) and then come right back to the callback?

My question is:

Is this a common pattern? Best-practice? Are there any advantages/disadvantages to either approach?

Jonathan.Brink
  • 23,757
  • 20
  • 73
  • 115
  • 1
    I don't have an answer, but I think your guess is correct, and that the use of an immediately resolved promise is just a clever use to do an update right away without timeouts as you said. I don't know about best practice for this case, but I use `Promise.resolve().then(...)` quite often to be sure to catch errors thrown in synchronous code that is used inside promise chains, so I don't think it's a bad choice to do. – Daniel B Jun 22 '16 at 21:18
  • "Wait until everything is rendered" - if this means rendering in terms of the browser observing style changes, `Promise.resolve().then(…)` isn't enough. As far as I know, there isn't a way to queue a function to run after rendering but before the next task. – JaffaTheCake Jun 01 '17 at 15:45

2 Answers2

18

Yes, Promise.resolve() does immediately fulfill with the undefined value that you implicitly passed in. The callback is still executed asynchronously - quite like in the setTimeout snippet you posted.

However, as the comment in the code explains, the intent is not just to execute the callback asynchronously:

Use a promise to make sure to be executed before timeouts may be executed.

Promise callbacks do run before timeouts or other events, and these subtle timing differences are sometimes important. Given that choice of the task loop is usually not important, No this is not a common pattern; but it is a valid pattern that does exactly what you need when you need it.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    For me, another reason to do it is the result is "then-able", so it makes a good stub for an asynch function I plan to develop later e.g.. `function codeMeLater() { return Promise.resolve("some day, I will compute this"); }` – danh Jun 22 '16 at 21:54
  • @danh: Oh, sure `Promise.resolve` is a very useful function in general, I only was refering to the specific pattern with the callback when I said that it's not common. – Bergi Jun 22 '16 at 21:56
  • Does this mean that the ES6 `Promise` API implements a micro-task mechanism [as stated here](http://stackoverflow.com/a/25933985/6445533). As far as I understand it, micro-tasks can lead to blocked UI in connection with recursion for instance. –  Jul 04 '16 at 16:04
  • @LUH3417: Yes they do - and as every feature you have to use them correctly. – Bergi Jul 04 '16 at 16:10
4

I noticed the technique in this polyfill: https://github.com/wicg/inert (with comment)

const newButton = document.createElement('button');
const inertContainer = document.querySelector('[inert]');
inertContainer.appendChild(newButton);
// Wait for the next microtask to allow mutation observers to react to the DOM change
Promise.resolve().then(() => {
expect(isUnfocusable(newButton)).to.equal(true);
});
Ollie_W
  • 337
  • 1
  • 5
  • 13