【问题标题】:How to pause the invocation of one function until another function is invoked independently?如何暂停一个函数的调用,直到另一个函数被独立调用?
【发布时间】:2019-04-21 05:32:16
【问题描述】:

我在下面有工作代码,但我觉得必须有一个更清洁的解决方案,我认为承诺或回调在我的情况下不起作用。

我有一个主函数 (foo),它在 x 毫秒后独立调用自己(在我的实际代码中,它会重复执行此操作)。当它调用自身时,它会增加一个存储在对象中的值。

我有第二个函数(bar),有时会被随机调用。但是,我希望(在某些情况下)能够暂停此函数的执行,直到下次再次调用主函数(foo)。

在下面的工作示例中,bar 在调用bar 时存储prop 的当前值,然后不断检查prop 的值,直到值发生变化。当值不再是 == 时,bar 执行else 中的代码。这可行,但并不干净,而且如果bar 只是监听prop 更改(或foo 被调用)并在那个时刻执行,那么时间也不准确。

我不相信 promise 或 callback 会起作用,因为它们通常用于将第二个函数作为参数传递的情况。就我而言,我的第二个功能是有另一份工作并且已经在重复做自己的工作。我也不希望再次执行第二个函数,我只想调用它以允许函数一完成自己的执行。

对于我的实际情况,使用延迟也不理想。

更多详情

我的实际程序通过随机触发音频文件播放来制作音乐。 bar() 是确定播放声音的所有方式/属性(持续时间、音量、淡入/淡出等)的函数。

foo() 是一个主函数,它决定程序在歌曲的哪个部分。所以每次调用foo() 时,它都会递增程序/歌曲所处的阶段(即,1、2、3, 4) 它还调用了一些函数,更新属性,改变了get() 函数被允许播放声音的方式。

因此,该程序主要随机播放声音(就像不是在刚性节拍/小节上),我希望在某些情况下能够在小节上播放它们。因为foo() 是跟踪这个时间的函数,所以我向get() 添加了功能,这样当一个节拍上没有触发声音时,它会延迟到指定的节拍发生,然后才允许播放。我有这个功能工作。

当一个声音被分配到一个已经过去的节拍上时,这个技巧就出现了。在这些情况下,我需要暂停 get() 直到下一个小节(即再次调用 foo() 时)。在正常的音乐情况下,时间(每分钟节拍数)将是固定的,这样我就可以简单地进行数学运算并将其设置超时,直到正确的时间。但是,我的程序有点非传统,每次调用foo() 时,持续时间都会发生变化。因此,下一个柱的持续时间是无法提前知道的。

因此,总而言之,bar()(声音)会被调用,其中包含一大堆参数,这些参数决定了声音的播放方式。我需要bar() 在它被调用后暂停,直到它听到/看到foo() 再次被调用,然后才完成播放声音的工作。

这是我的工作示例。

const config = {
  object: {
    prop: 1
  },
};

(function foo() {
  setTimeout(function() {
    config.object.prop = 3;
    console.log('foo changed the value of config.object.prop');
  }, 4000);
})();

bar();

function bar() {

  var prop = config.object.prop;

  (function nar() {
    if (prop == config.object.prop) {
      console.log('sorry, still waiting for config.object.prop to change');
      setTimeout(function() {
        nar();
      }, 1000);
    } else {
      console.log('Executed only after config.object.prop changed!!!');
    }
  })();
}

【问题讨论】:

    标签: javascript promise callback


    【解决方案1】:

    这是回调和观察者模式的完美用例。

    您的主进程应该产生节拍事件,并触发等待它们的函数。

    您可以使用一个消息总线框架来帮助您解决这个问题,或者您甚至可以使用一个函数来获得从主进程发生下一个节拍的承诺,但是本土解决方案也可以工作。您只需要一组侦听器函数,bar 将功能的实时部分添加到该列表中,foo 调用(并删除)该列表中的函数。

    【讨论】:

    • 听起来你在暗示我需要重新调整我的方法。我目前有触发声音的独立函数(如果可能的话,我想保持这种方式)。你的意思是我必须让主函数完成所有的触发,不是吗?我的方法不可能吗?
    • 另外,观察者模式让我有点难以置信。当主题通知订阅的观察者时,它是否也可以返回一些可以导致观察者函数最终完成并触发示例播放的东西(一个值?)?我怀疑这就是你建议我将观察者与回调配对的原因。但如果是这样的话,我不确定我是否需要观察者模式。我有一种感觉,我在问什么是毫无意义的,但也许你可以理顺我。谢谢
    • 您应该为各个声音保留独立的功能。但是,不要以某种方式“等待”主功能栏,尤其是通过轮询标志,您应该使用观察者模式或某种类似形式的消息传递。主函数不知道它触发的什么,只知道它触发的何时 - 任何东西都可以订阅。
    • 不,在传统的观察者模式中,订阅者不返回任何东西,它们是无效函数。我不确定您所说的“导致观察者功能最终完成”是什么意思。但是,如果返回要播放的声音对您来说更容易,您可能会偏离传统。
    【解决方案2】:

    你可以有一个布尔值来决定每次执行 foo() 时是否应该调用 nar() 并让 bar() 在函数需要运行时将布尔值设置为 true。

    let runNar = false;
    
    (function foo() {
      setTimeout(function() {
    	if (runNar === true) {
    		nar();
    		runNar = false;
    	}
        console.log('foo changed the value of config.object.prop');
      }, 4000);
    })();
    
    bar();
    
    function bar() {
    	runNar = true;
    };
    
    function nar() {
    	console.log('Excuted when foo() is called');
    };

    【讨论】:

      【解决方案3】:

      也可以使用 Promises 解决,但我认为有更好的解决方案:
      使用setters 或Proxy!

      它是如何工作的?

      • 二传手:
        设置器可以定义不能有值的伪属性,但是如果您尝试设置该属性的值,则会调用一个函数,接收新值作为参数。

      语法:

      var obj={
        prop:1,
        set anotherProp(value){
          //Handle set:
          this.prop=value
        }
      }
      obj.anotherProp=5
      obj.prop //5
      

      翻译成您的案例:

      const config = {
        object: {
          prop: 1,
          set setterProp(value){
            this.prop=value;
            bar()
          }
        },
      };
      
      (function foo() {
        setTimeout(function() {
          console.log('foo changed the value of config.object.prop');
          config.object.setterProp = 3;
        }, 4000);
      })();
      
      function bar() {
          console.log('Executed only after config.object.prop changed!!!');
      }
      • 代理: 使用 Proxy 对象,您可以执行 setter 之类的操作,但是:
        1. 代理可用于监视和控制对对象的任何操作(获取、设置、删除、函数调用、类构造等)
        2. 与 setter 不同,Proxy 监视对象的所有属性。

      语法:

      var obj={
        prop:1
      }
      var proxy=new Proxy(obj,{
        set:function(obj,prop,value){
          //Handle set:
          obj[prop]=value;
        }
      })
      proxy.prop=5
      proxy.prop //5
      obj.prop   //5
      

      翻译成您的案例:

      const config = {
        object: {
          prop: 1
        },
      };
      const proxy=new Proxy(config.object, {
        set: function(obj,prop,value){
          //Handle set:
          obj[prop]=value;
          if(prop==='prop'){bar()};
        }
      });
      (function foo() {
        setTimeout(function() {
          console.log('foo changed the value of config.object.prop');
          proxy.prop = 3;
        }, 4000);
      })();
      
      function bar(){
        console.log('Executed only after config.object.prop changed!!!');
      }

      好的,但是如果我不想每次都调用 bar 怎么办!

      const barList=[]
      const config = {
        object: {
          prop: 1
        },
      };
      const proxy=new Proxy(config.object, {
        set: function(obj,prop,value){
          //Handle set:
          obj[prop]=value;
          if(prop==='prop'){
            barList.filter(x=>{
              bar(...x)
              return false
            })
          };
        }
      });
      (function foo() {
        setTimeout(function() {
          console.log('foo changed the value of config.object.prop');
          proxy.prop = 3;
        }, 4000);
      })();
      function callBar(...args){
        barList.push(args)
      }
      function bar(...argument){
        console.log('Executed only after config.object.prop changed!!!', ...argument);
      }
      callBar();
      callBar('with','some','arguments');

      【讨论】:

      • 感谢您提供如此详尽的回答。我想我现在了解了 getter 和 setter。代理仍然让我难以置信。如果我正确理解您的想法,则有一个方面不适合我的需求。似乎您正在将调用 bar() 的控制权交给 foo()。我认为这不适用于我的情况,因为我之前已经调用了 get() 。我需要 get() 成为控制监听何时调用 foo() 的函数,以便在调用 get() 之后,当 foo() 执行它的操作时,只有这样 get() 才能简单地完成它正在做。
      • @forestkelley 抱歉,我无法完全理解您的情况...get() 是什么,它有什么作用?
      • 没问题@FZs 我还是学到了不少东西。我打算在上一篇文章中写bar() 而不是get()。对不起!如果允许我编辑,我会更新它。为了回答您的问题,我在上面问题的“更多详细信息”部分中添加了对程序和我的需求的更多解释。谢谢!
      • 这里不需要代理。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多