用于处理多个未知回调的Javascript函数

时间:2017-05-29 21:30:37

标签: javascript function recursion callback

我有一个场景,我希望将2个或更多函数(作为参数)发送到处理函数中,并让该处理函数执行每个传递的函数作为前一个函数的回调函数。

以下是我要编写的函数的一般概念:

function functionChain() {
   // MAKE SURE WE HAVE AT LEAST 1 PARAMETER
   if ( arguments.length < 1 ) { return; }

   // for each parameter, call it (as a function)
   for ( var i=0; i<arguments.length; i++) {
     if ( typeof arguments[i] === 'function' ) {    
       call arguments[i];
     }
   }
}
// example
functionChain( function1, function2, function3 );

...所以在上面的代码中,将连续调用每个函数。

我遇到的问题是如何在前一个函数完成时将每个调用视为回调。

我接近这个的方法是有一个变量(为简单起见,我们只说一个名为functionChainComplete的全局变量),然后等待启动下一个函数 - 当然,我调用的每个函数都会将functionChainComplete设置为true 。所以,像这样:

// set global var for tracking
var functionChainComplete;

function functionChain() {
   // MAKE SURE WE HAVE AT LEAST 1 PARAMETER
   if ( arguments.length < 1 ) { return; }

   // SET GLOBAL VAR TO FALSE
   functionChainComplete = true;

   // for each parameter, call it (as a function)
   for ( var i=0; i<arguments.length; i++) {
     if ( typeof arguments[i] === 'function' ) {    
       if ( functionChainComplete == true ) {
         // call the next function and wait for true again
         functionChainComplete = false;
         call arguments[i];
       } else {
         // try again in 50 ms (maybe setTimeout)?
       }
     }
   }
}

function1() { 
    // do something, and when done, reset functionChainComplete
    functionChainComplete = true;
}

function2() { 
    // do something, and when done, reset functionChainComplete
    functionChainComplete = true;
}

function3() { 
    // do something, and when done, reset functionChainComplete
    functionChainComplete = true;
}

// example
functionChain( function1, function2, function3 );

正如你所看到的,上面的代码没有解决回调片段,我不知道从这里把它带到哪里 - 我怀疑某种递归函数?我被卡住了。

3 个答案:

答案 0 :(得分:1)

假设你有一些函数double,它接受​​一个参数,x和一个回调,k

const double = (x, k) =>
  k(x * 2)
  
double(2, console.log) // 4
double(3, console.log) // 6

现在说我们要连续3次运行它

const double = (x, k) =>
  k(x * 2)
      
const tripleDouble = (x, k) =>
  double(x, y =>
    double(y, z =>
      double(z, k)))
      
tripleDouble(2, console.log) // 16
tripleDouble(3, console.log) // 24

但当然我们必须对每个延续(y => ...z => ...)进行静态编码。我们如何使用变量(数组)函数来完成这项工作?

const double = (x, k) =>
  k(x * 2)
  
const composek = (...fs) => (x, k) =>
  fs.reduce((acc, f) =>
    k => acc(x => f(x, k)), k => k(x)) (k)
  
const foo = composek(double, double, double)

foo(2, console.log) // 16
foo(3, console.log) // 24

这对于一些抽象来说已经成熟,并介绍了我最喜欢的monad,即Continuation Monad。

const Cont = f => ({
  runCont: f,
  chain: g =>
    Cont(k => f(x => g(x).runCont(k)))
})

Cont.of = x => Cont(k => k(x))

const composek = (...fs) => (x, k) =>
  fs.reduce((acc,f) =>
    acc.chain(x =>
      Cont(k => f(x,k))), Cont.of(x)).runCont(k)
      
const double = (x, k) =>
  k(x * 2)
  
const foo = composek(double, double, double)

foo(2, console.log) // 16
foo(3, console.log) // 24

如果你有自由改变你正在链接的功能,这会更加清理一点 - 在这里,double有1个参数并返回Cont而不是作为第二个回调参数

const Cont = f => ({
  runCont: f,
  chain: g =>
    Cont(k => f(x => g(x).runCont(k)))
})

Cont.of = x => Cont(k => k(x))

// simplified
const composek = (...fs) => (x, k) =>
  fs.reduce((acc,f) => acc.chain(f), Cont.of(x)).runCont(k)

// simplified
const double = x =>
  Cont.of(x * 2)
  
const foo = composek(double, double, double)

foo(2, console.log) // 16
foo(3, console.log) // 24

当然,如果double实际上是异步的,那么它将是相同的

// change double to be async; output stays the same
const double = x =>
  Cont(k => setTimeout(k, 1000, x * 2))

const foo = composek(double, double, double)

foo(2, console.log) // 16
foo(3, console.log) // 24

答案 1 :(得分:0)

这样的东西? (见评论,但相当不言自明。)

function functionChain() {
    var args = arguments;

    // MAKE SURE WE HAVE AT LEAST 1 PARAMETER
    if ( args.length < 1 ) { return; }

    // Start the process
    var i = -1;
    go();

    function go() {
        // Pre-increment so we start at 0
        ++i;
        if (i < args.length) {
            // We have a next function, do it and continue when we get the callback
            args[i](go);
        }
    }
}

示例:

function functionChain() {
    var args = arguments;
    
    // MAKE SURE WE HAVE AT LEAST 1 PARAMETER
    if ( args.length < 1 ) { return; }

    // Start the process
    var i = -1;
    go();

    function go() {
        // Pre-increment so we start at 0
        ++i;
        if (i < args.length) {
            // We have a next function, do it and continue when we get the callback
            args[i](go);
        }
    }
}

// Just some functions for an example:
function a(callback) {
  console.log("a");
  callback();
}

function b(callback) {
  console.log("b");
  callback();
}

// Note this one is async
function c(callback) {
  setTimeout(function() {
    console.log("c");
    callback();
  }, 100);
}

function d(callback) {
  console.log("d");
  callback();
}

functionChain(a, b, c, d);

也就是说,承诺的原因之一是允许编写可能异步的函数。如果你的函数返回了promises,我们将使用reduce成语:

function functionChain() {
    // Assumes the functions return promises (or at least thenables)
    Array.prototype.reduce.call(arguments, function(p, f) {
      return p.then(f);
    }, Promise.resolve());
}

function functionChain() {
    Array.prototype.reduce.call(arguments, function(p, f) {
      return p.then(f);
    }, Promise.resolve());
}

// Just some functions for an example:
function a(callback) {
  return new Promise(function(resolve) {
    console.log("a");
    resolve();
  });
}

function b(callback) {
  return new Promise(function(resolve) {
    console.log("b");
    resolve();
  });
}

// Note this one has a delay
function c(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      console.log("c");
      resolve();
    }, 100);
  });
}

function d(callback) {
  return new Promise(function(resolve) {
    console.log("d");
    resolve();
  });
}

functionChain(a, b, c, d);

答案 2 :(得分:0)

可以使用nsynjs

完成此操作
  1. 将带有回调的所有慢速函数包装到nsynjs-aware包装器中(参见wait()),
  2. 将您的逻辑置于函数中,就像它是同步的一样(参见synchronousCode()),
  3. 通过nsynjs引擎运行该功能(请参阅nsynjs.run())
  4. <script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
    <script>
    		var wait = function (ctx, ms) {
    			setTimeout(function () {
    				console.log('firing timeout');
    				ctx.resume();
    			}, ms);
    		};
    		wait.nsynjsHasCallback = true;
    
    		function synchronousCode() {
    			function function1() { 
    				console.log('in function1');
    				wait(nsynjsCtx,1000);
    			};
    
    			function function2() { 
    				console.log('in function2');
    				wait(nsynjsCtx,1000);
    			};
    
    			function function3() { 
    				console.log('in function3');
    				wait(nsynjsCtx,1000);
    			};
    			
    			
    			function functionChain() {
    			   // MAKE SURE WE HAVE AT LEAST 1 PARAMETER
    			   if ( arguments.length < 1 ) return;
    
    			   for ( var i=0; i<arguments.length; i++) {
    				 //console.log(i,arguments[i]);
    				 if ( typeof arguments[i] === 'function' ) {    
    					 arguments[i]();
    				 };
    			   };
    			};
    			
    			functionChain(function1,function2,function3);
    		}
    		
    		nsynjs.run(synchronousCode,{},function(){
    			console.log("Synchronous Code done");
    		})
    	</script>

    有关更多示例,请参阅https://github.com/amaksr/nsynjs/tree/master/examples

相关问题