Javascript 在执行器函数内侦听承诺何时解析

Javascript 在执行器函数内侦听承诺何时解析,javascript,node.js,es6-promise,Javascript,Node.js,Es6 Promise,想象一下,我想确保从executor函数内部清理资源 我想这样做: return new Promise(function(resolve, reject { const to = setTimeout(function(){ }); }); // can be used as either: // let d = Promise.Deferred(); // let d = new Promise.Deferred(); // d.then

想象一下,我想确保从executor函数内部清理资源

我想这样做:

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

      });
  });
// 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);
}
但是,
在承诺执行器函数中不可用。 有什么方法可以清理承诺执行器中的异步资源吗


我想您可以在调用resolve/reject之前清除它们,但在某些情况下这更难。

不确定是否需要在超时触发后清除超时,但您可以尝试以下操作:

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

      });
      this.finally(function(){
        clearTimeout(to);
      });
  });
var someTest = () => {
  var t;
  var p = new Promise(
    (resole)=>{
      t = setTimeout(resole,2000)
    }
  );
  p.finally(
    ()=>console.log(t,clearTimeout(t))
  )
  return p;
}
someTest();
或者您可以尝试以下方法:

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

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

不确定是否需要在触发超时后清除超时,但可以尝试以下操作:

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

      });
      this.finally(function(){
        clearTimeout(to);
      });
  });
var someTest = () => {
  var t;
  var p = new Promise(
    (resole)=>{
      t = setTimeout(resole,2000)
    }
  );
  p.finally(
    ()=>console.log(t,clearTimeout(t))
  )
  return p;
}
someTest();
或者您可以尝试以下方法:

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

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

从承诺执行人内部,您无法访问承诺。它还没有被分配到你的代码可以到达的任何地方

所以,你有两个选择

  • 您可以将清理代码放在executor之外,在那里您可以使用
    p.finally()
    访问返回的承诺。然后,您还必须跟踪执行者之外的资源(这可能不方便)
  • 您可以用自己的执行清理的存根替换
    resolve()
    reject()
    回调,然后调用实际的
    resolve()
    reject()
  • 您可以使用一个延迟对象,该对象允许您从承诺执行器外部调用resolve/reject,从而允许您访问
    p.finally()
    resolve()
    reject()
    ,所有这些都在同一范围内(这实际上是原始质询的来源)
  • 下面是选项2的一个示例:


    下面是选项3的一个示例

    通常不建议使用延迟对象,但它们确实可以让您访问
    .finally()
    resolve()
    reject()
    所有这些对象都在同一范围内,这会使某些事情变得更干净(比如您正在尝试做的事情)

    首先是一个简单的承诺包装器,它为我们提供了一个延迟对象:

    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);
        }
    });
    
    然后,你可以这样使用它:

      return new Promise(function(resolve, reject {
          const to = setTimeout(function(){
    
          });
      });
    
    // 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);
    }
    

    从承诺执行人内部,您无法访问承诺。它还没有被分配到你的代码可以到达的任何地方

    所以,你有两个选择

  • 您可以将清理代码放在executor之外,在那里您可以使用
    p.finally()
    访问返回的承诺。然后,您还必须跟踪执行者之外的资源(这可能不方便)
  • 您可以用自己的执行清理的存根替换
    resolve()
    reject()
    回调,然后调用实际的
    resolve()
    reject()
  • 您可以使用一个延迟对象,该对象允许您从承诺执行器外部调用resolve/reject,从而允许您访问
    p.finally()
    resolve()
    reject()
    ,所有这些都在同一范围内(这实际上是原始质询的来源)
  • 下面是选项2的一个示例:


    下面是选项3的一个示例

    通常不建议使用延迟对象,但它们确实可以让您访问
    .finally()
    resolve()
    reject()
    所有这些对象都在同一范围内,这会使某些事情变得更干净(比如您正在尝试做的事情)

    首先是一个简单的承诺包装器,它为我们提供了一个延迟对象:

    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);
        }
    });
    
    然后,你可以这样使用它:

      return new Promise(function(resolve, reject {
          const to = setTimeout(function(){
    
          });
      });
    
    // 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);
    }
    

    本OP描述了imo承诺中的一个更丑陋的混乱,但这可能会起作用:

    // 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;
    }
    

    此解决方案的问题是then中的回调被称为异步,因此计时器可能会在中间解决?不确定。

    这篇文章描述了imo承诺中一个更丑陋的混乱,但这可能会奏效:

    // 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;
    }
    

    此解决方案的问题是then中的回调被称为异步,因此计时器可能会在中间解决?不确定。

    我认为您可以多次调用同一超时对象上的clearTimeout,而不会发生太多不好的事情……我不认为最终调用的布尔值是这样的necessary@AlexanderMills-是的,你可以,但我想提出一个更通用的设计,确保清理代码只调用一次。如果您所做的只是调用
    clearTimeout()
    ,然后是的,您可以删除布尔值。@AlexanderMills-我添加了基于延迟对象的第三个选项。我认为您可以多次调用同一超时对象上的clearTimeout,而不会发生太多不好的事情……我不认为最终调用的布尔值是这样的necessary@AlexanderMills-是的,你可以,但我想提出一种更通用的设计,确保清理代码只调用一次。如果您所做的只是调用
    clearTimeout()
    ,那么可以删除布尔值。@AlexanderMills-我添加了基于延迟对象的第三个选项。