Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for value to be defined without setTimeout?

I'm grabbing a value from somewhere and wanting to use it locally (using lodash):

const setting = _.get(this.settings, 'options');

this.settings.options is set somewhere else, and depending on the environment, this.settings.options may be undefined. In that case, I can do something like:

const set = (setting) => {
  ...
  ...
};

const getThing = () => {
  setTimeout(() => {
    const setting = _.get(this.settings, 'options');
    return setting ? set(setting) : getThing();
  }, 1000);
};

getThing();

This uses a setTimeout function to wait 1 second for this.settings.options to be set in that time, and if it's not yet set, the setTimeout function calls itself to check one second later. After that, it continues to the set() function with the acquired data. This seems to work every time, but it would be really nice if I could keep checking for this value until it is defined without a timer. I'm not sure if that's possible?

I've been trying to implement either a promise or use async / await to do this, but the examples I've seen also seem to use setTimeouts, and the solution ends up seemingly more complicated. Among others, I've mainly been looking at this answer. Regardless, I'm having trouble doing this with async / await and will have to continue troubleshooting that.

Is there a way to wait for this value to be defined without using a setTimeout or setInterval?

like image 937
rpivovar Avatar asked Sep 10 '19 22:09

rpivovar


People also ask

How do you wait for function to finish?

– Async Function-Based Promises: Use the “Await” Function For async function-based promises, you may add the “await” function to stop the function in javascript, until the promise is fulfilled, and then return the result value.

How do you wait for setTimeout?

Use of setTimeout() function: In order to wait for a promise to finish before returning the variable, the function can be set with setTimeout(), so that the function waits for a few milliseconds. Use of async or await() function: This method can be used if the exact time required in setTimeout() cannot be specified.


1 Answers

Unfortunately, there is no straightforward way to do it.

If you can mutate this.settings (and this.settings.options is at least configurable), then you can define a setter, and wait for it to be triggered.

This is generally an ugly pattern (use it only if there's no other way), but still better than periodically checking the existence of a property.

Also note that this will work only if the mysterious code that sets .options uses [[Set]] (i.e., obj.prop = value or Reflect.set), and not [[Define]] (i.e., Object.defineProperty).

if(typeof this.settings.options==='undefined'){
  //Define a setter
  Object.defineProperty(this.settings, 'options', {
    set: (value)=>{
      //Remove setter, and transform to simple property
      Object.defineProperty(this.settings, 'options', {value})
      //Hypothetical function to be called with the result
      doSomethingWithTheValue(value)
    },
    configurable: true,
    enumerable: true
  })
}else{
  doSomethingWithTheValue(this.settings.options)
}

And, you can now easily rewrite this to a general function that uses Promises (and therefore supports async/await):

function getAsync(obj, prop){
  return new Promise((resolve,reject)=>{
    if(typeof obj[prop]==='undefined'){
      Object.defineProperty(obj, prop, {
        set: (value)=>{
          Object.defineProperty(obj, prop, {value})
          resolve(value)
        },
        configurable: true,
        enumerable: true
      })
    }else{
      resolve(obj[prop])
    }
  })
}

getAsync(this.settings,'options')
  .then(doSomethingWithTheValue)

//or, async/await:

(async()=>{
  const value=await getAsync(this.settings,'options')
  doSomethingWithTheValue(value)
})
like image 84
FZs Avatar answered Nov 06 '22 17:11

FZs