简体   繁体   中英

Listen for when promise resolves inside executor function

  return new Promise(function(resolve, reject {
      const to = setTimeout(function(){

      });
  });

imagine that I want to make sure resources are cleaned up from inside the executor function.

I would like to do something like this:

  return new Promise(function(resolve, reject {
      const to = setTimeout(function(){

      });
      this.finally(function(){
        clearTimeout(to);
      });
  });

but this is not a available in a promise executor function. Is there some way to clean up async resources in a promise executor?

I guess you could clean them up before call resolve/reject, but there are a few cases where that is harder.

Not sure if you need to clear the timeout after it fired but you could try the following:

var someTest = () => {
  var t;
  var p = new Promise(
    (resole)=>{
      t = setTimeout(resole,2000)
    }
  );
  p.finally(
    ()=>console.log(t,clearTimeout(t))
  )
  return p;
}
someTest();

Or you could try the following:

var someTest = () =>
  new Promise(
    (resole)=>{
      t = setTimeout(resole,2000)
    }
  ).then(
    result=>{
      //clean up
      return result
    },
    error=>{
      //clean up
      return Promise.reject(error)
    }
  );

From inside the promise executor, you can't get access to the promise. It hasn't been assigned yet to anything that your code can reach.

So, you have two options.

  1. You can put your cleanup code outside the executor where you can access the returned promise with p.finally() . You will then have to also keep track of your resources outside the executor also (which is perhaps inconvenient).
  2. You can replace the resolve() and reject() callbacks with your own stub that does your cleanup, then calls the actual resolve() or reject() .
  3. You can use a Deferred object which allows you to call resolve/reject from outside the promise executor thus giving you access to p.finally() and resolve() and reject() all in the same scope (which is really the source of the original challenge).

Here's an example of option #2:

return new Promise(function(rv, rj) {
    // have to try/catch here because an execption will automatically reject
    // without us having seen it
    try {
        // declare wrappers that should be called by code in this executor
        // do not call rv() and rj() directly
        function resolve(arg) {
            finally();
            rv(arg);
        }
        function reject(arg) {
            finally();
            rj(arg);
        }

        // cleanup code that is only ever called once
        let finallyCalled = false;
        function finally() {
            if (!finallyCalled) {
                clearTimeout(to);
                finallyCalled = true;
            }
        }

        const to = setTimeout(function(){

        });

        // elsewhere in this executor it should call resolve() or reject()

    } catch(e) {
        reject(e);
    }
});

Here's an example of option #3.

Deferred objects are generally not recommended, but they do give you access to .finally() , resolve() and reject() all in the same scope which does make some things cleaner (like what you're trying to do).

First a simple promise wrapper that gives us a Deferred object:

// can be used as either:
//    let d = Promise.Deferred();
//    let d = new Promise.Deferred();
//    d.then(...)
//    d.resolve(x);
//    d.finally(...)
Promise.Deferred = function() {
    if (!(this instanceof Promise.Deferred)) {
        return new Promise.Deferred();
    }
    let p = this.promise = new Promise((resolve, reject) => {
        this.resolve = resolve;
        this.reject = reject;
    });
    this.then = p.then.bind(p);
    this.catch = p.catch.bind(p);
    this.finally = p.finally.bind(p);
}

Then, you could use it like this:

// usage
function yourFunction() {
    let d = new Promise.Deferred();
    const to = setTimeout(...);

    // other code here that will call d.resolve() or d.reject()

    // cleanup code
    d.finally(function() {
        clearTimeout(to);
    });
    return d.promise;
}

This OP describes one of the uglier snafus with Promises imo, but this might work:

 return new Promise(function(resolve, reject {
      const to = setTimeout(function(){
           console.error('timed out');
           reject('timed out');
      });

      doSomething(function(err, data){
           if(!to._called){
            resolve({to, data})
           } 
      });
  })
  .then(function(v){
       clearTimeout(v && v.to);
       return v && v.data;
   });

the problem with this solution is that callback in the then is called asynchronous, so it might be possible that the timer resolves in the interim? not sure.

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