简体   繁体   中英

Resolving a Promise that calls own function

if I call this function, it doesn't seem to work. what it's doing is just waiting for global variable "window.AppApi" to be initialized before doing things

i feel like maybe it's because i'm calling the function again in the time out? is there something I'm missing to make this work? if it's not possible what would be a good alternative..

initializeApp()
  .then(( result ) => {
    console.log( 'it worked!' );  // does not go here
  });


export const initializeApp = () => {
  return new Promise(( resolve, reject ) => {
    // wait for App API to be initialized
    if ( window.AppApi ) {
      console.log( 'App initialized.' );
      resolve( true );
    }
    else {
      console.log( 'waiting for App to initialize...' );
      setTimeout( () => initializeApp(), 250 );
    }
  });
};

Technically you can do it even without dirty timeouts with old good Object.defineProperty setter:

 const initializeApp = () => { return new Promise((resolve, reject) => { if (window.AppApi) { resolve(true); return; } Object.defineProperty(window, 'AppApi', { set (value) { console.log('App initialized.'); resolve(true); return value } }) }); }; initializeApp() .then((result) => { console.log('it worked!'); // does not go here }); setTimeout(() => { window.AppApi = { test: 123 } }, 2000) 

The problem is that the setTimeout( () => initializeApp(), 250 ) will create a new Promise that is not used anywhere, and the first Promise that is created - and which is the only relevant for your first code block - will only be resolve if window.AppApi is set before the first call of initializeApp .

You would have to write somethign like this:

export const initializeApp = () => {
    return new Promise((resolve, reject) => {

      // an "inner" function that creates a closure over resolve
      // and which does the check if window.AppApi is set
      function checkIfApiLoaded() {
        if (window.AppApi) {
          console.log('App initialized.');
          resolve(true);
        } else {
          console.log('waiting for App to initialize...');

          // if window.AppApi was not set call checkIfApiLoaded with a delay,
          // because of the closure it will still know the 'resolve' function
          setTimeout(checkIfApiLoaded, 250);
        }
      }

      // initially call this inner function
      checkIfApiLoaded()
    });

Instead of polling for this value, you could define it as a getter that resolves the promise:

 const initializeApp = () => new Promise(resolve => { const { AppApi } = window; if (AppApi) return resolve(AppApi); Object.defineProperty(window, 'AppApi', { get () { return AppApi; }, set (value) { console.log('App initialized.'); Object.defineProperty(window, 'AppApi', { value, enumerable: true, configurable: true, writable: true }); resolve(value); }, enumerable: true, configurable: true }); }); initializeApp().then(AppApi => { console.log(AppApi); }); setTimeout(() => window.AppApi = { test: 123 }, 1000); 

This uses Object.defineProperty() to create a property accessor that resolves the promise immediately when the API is flagged as ready. This also has the added benefit of resolving with the value of AppApi so if it's a namespace you'll have it available as the first argument of the callback function for .then() .

I think the issue is that your initializeApp function creates a new promise each time it's called. So when it's called recursively, the original promise is lost and will never resolve. Instead, put the recursive part into its own sub-function, and call that recursively:

 const initializeApp = () => { const resolver = (res) => { if (window.AppApi) { res(true); } else { setTimeout(resolver, 250, res); } } return new Promise(( resolve, reject ) => { resolver(resolve) }); }; initializeApp().then(( result ) => { console.log( 'it worked!' ); }); setTimeout(() => { console.log("Setting api"); window.AppApi = {} }, 2000); 

Since window.AppApi is a value that is available in the future you may think of an object that represents that.....

A promise.

If window.AppApi is set to promise of AppApi then when you want to use it you can do:

window.AppApi.then(...

But in order to do that you should not show initializeApp but the code that set AppApi and how you do that. Because it seems that initializeApp solves a problem (quite elegantly in some answers) that should not exist in the first place.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM