简体   繁体   中英

Prevent binding a function more than once

I have a recursive retry routine, something like this:

Foo.prototype.retry = function(data, cb){

   cb && (cb = cb.bind(this));  // want to bind the cb fn just once

   this.resolutions[x] = (err, val) => {

      if(val === 'foobar'){
        // in this case, we do a retry
        return this.retry(data, cb);
      }

   }:

}

as you can see, under certain circumstances, I will retry by calling this.run again. But I would like to avoid calling cb.bind() more than once. Is there any good way to do that?

=> What I mean, is there a way to somehow check a function is bound to a certain this value?

The only good solution I know of is to pass a retry count, like so:

 Foo.prototype.retry = function(data, cb){

       if(cb){
         if(!data.__retryCount){
             cb = cb.bind(this);
         }
       }

       this.resolutions[x] = (err, val) => {

          if(val === 'foobar'){
            // we do a retry here
            data.__retryCount = data.__retryCount || 0;
            data.__retryCount++;
            return this.retry(data, cb);
          }

       }:

    }

You can use a local variable for the bound version so when you call yourself recursively, you pass the original cb , not the bound one:

Foo.prototype.run = function(data, cb){

   let callback = (cb && cb.bind(this)) || function() {};

   this.resolutions[x] = (err, val) => {
      if(val === 'foobar'){
        // we do a retry here and pass original cb
        return this.run(data, cb);
      }
   };

   // then elsewhere in this function when you want to use the bound one, use callback()
}

Or, if you really only want to ever bind it once, then you can do that in a wrapper function and call yourself recursively via a sub-function that assumes the callback is already bound:

// internal function, assumes callback is already bound
Foo.prototype._run = function(data, cb){
    // cb is already bound here
    this.resolutions[x] = (err, val) => {
        if(val === 'foobar'){
        // we do a retry here
            return this._run(data, cb);
        }
   }

}

// bind the callback and then call our internal function
Foo.prototype.run = function(data, cb){
   let callback = (cb && cb.bind(this)) || function() {};
   return this._run(data, callback);
}

You can create a class variable which indicates whether or not the function has been bound:

 let Foo = function() { this.resolutions = []; }; Foo.prototype.run = function(data, cb) { if (!this.bound) { console.log('binding'); cb && (cb = cb.bind(this)); this.bound = true; } this.resolutions[x] = (err, val) => { if (val === 'foobar') { // we do a retry here return this.run(data, cb); } }; }; console.log('x'); let x = new Foo(); x.run(); console.log('y'); let y = new Foo(); y.run(); console.log('x'); x.run(); 

Since binding obscures the origional plain-text function source code from the Function.toString() method, you can inspect the string version to see if a user-land function has been bound:

 if(!/\[native code\]/.test(cb)) cb = cb.bind(this);

Note you can't use this approach on already native methods like console.log or window.alert , but that's probably not an issue for your use-case.

in whole:

 Foo.prototype.retry = function(data, cb){
   if(!/\[native code\]/.test(cb)) cb = cb.bind(this); //  bind the cb fn just once
   this.resolutions[x] = (err, val) => {
      if(val === 'foobar'){
        // in this case, we do a retry
        return this.retry(data, cb);
      }
   }
};

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