我们可以有一个调度程序,你可以异步添加东西,但会同步按顺序执行吗?

时间:2013-06-23 19:02:38

标签: javascript asynchronous

作为我的另一个问题的“分支”问题(请参阅编辑4)How to convert a png base64 string to pixel array without using canvas getImageData?

我想知道当你有这样一组函数时会发生什么:

function convertTileToRGB(tile,whatToDoNext){
    tile.img.onload = function() {
        //async code returns pixels
        whatToDoNext(pixels);
    }

}

function doSomethingWithTile(tile,params){
    convertTileToRGB(tile,function(pixels){
        //manipulate pixels in tile
    });
}

function doSomethingWithTile2(tile,params){
    convertTileToRGB(tile,function(pixels){
        //manipulate pixels in a different way
    });
}

有没有像这样的任意调用序列,

var tile = initialize_tile();

doSomethingWithTile(tile,params1)
doSomethingWithTile2(tile,params2)
doSomethingWithTile(tile,params3)  
...

这是的动态和预先未知的,换句话说加入异步到调度器的程序运行时,然后调度程序是同步调用它们,以某种方式解决了吗?

如果我们没有调度程序并且我们使用回调,我们可以看到这些方法在初始化的tile上单独起作用,但是它们的动作没有累积到tile中。在该示例中,保留了顺序,但实际上下一个异步方法的结果不会用于下一个。

提前感谢您提供任何帮助

修改 谢谢你的回答。是的,订单很重要,但是这些事情的清单在整个过程中都在变化(更多的东西被添加)并且事先不知道。如果有一种方法可以让一个调度程序对象具有异步添加的特定顺序,那么它会同步调用它们。我相应地改变了我的问题,因为大多数人都很困惑。

EDIT2: 试图更改我的代码: 调度员看起来如下。对于每个调用,tile和sample可以是不同的,但对于某些调用可能是相同的。除addSample外,将应用其他操作。如果使用相同的图块,则应该按照该顺序在图块中累积调用更改。最重要的是,调度员应该能够获得更多的异步操作。

var sample = [[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f],[0x7f,0x7f,0x7f]]
var tile = initialize_tile({width: 7,height: 7,defaultColor: [0x00,0xAA,0x00], x: 1, y: 2, zoomLevel: 1});
var dispatcher = { 
    0:{
        func: addSample, 
        tile: tile,
        sample: sample, 
        num_samples: 6, 
        which_sample: 1
    },
    1:{
        func: addSample, 
        tile: tile,
        sample: sample, 
        num_samples: 6, 
        which_sample: 2
    },
       2:{
               func: render,
               tile: tile,
         }
};
var disp = dispatcher[0];
disp.func(disp.tile,disp.sample,disp.num_samples,disp.which_sample);    
disp = dispatcher[1];
disp.func(disp.tile,disp.sample,disp.num_samples,disp.which_sample);

使用该代码时,不会保留顺序,并且不会为下一次调用保留该图块的内容。

6 个答案:

答案 0 :(得分:2)

如果您希望能够以定义的顺序链接doSomething调用,唯一的方法是允许doSomething在完成时发出信号,类似于convertTileToRGB的执行方式:

function doSomethingWithTile(tile,params, onDone){
    convertTileToRGB(tile,function(pixels){
        //manipulate pixels in tile
        onDone();
    });
}

最后,您的代码看起来像

doSomethingWithTile(tile,params1, function(){
    doSomethingWithTile2(tile,params2, function(){
        doSomethingWithTile(tile,params3, function(){
           //...
        });
    });
});

现在代码保证以正确的顺序运行。 “厄运金字塔”模式有点难看,所以你可能想找到一种方法,通过使用命名函数而不是匿名函数来回调或使用“音序器”库函数:

 //There are lots of async control flow libraries available for Javascript.
 // All of them should allow you to write code sort of like this one:
sequencer([
    function(onDone){ doSomethingWithTile(tile,params1, onDone) },
    function(onDone){ doSomethingWithTile(tile,params2, onDone) },
    function(onDone){ doSomethingWithTile(tile,params3, onDone) }
], function(){
    //...
});

你也可以更动态地做事:

function render_stuff(argss, onDone){

    var i = 0;
    function loop(){
        if(i < argss.length){
            var args = argss[i];
            addSample(args.tile, args.sample, args.num_samples, args.which_sample, function(){
               i = i + 1;
               loop();
            });
        }else{
            render(tile, onDone);
        }
    }

    loop();
}

render_stuff([
   {tile:tile, sample:sample, num_samples:6, which_sample:1},
   {tile:tile, sample:sample, num_samples:6, which_sample:2}
], function(){
    console.log("done rendering");
 });

如果这些函数不是异步的,那么这将等同于某些代码,如

function render_stuff(argss){
    i = 0;
    while(i < argss.length){
      var args = argss[i];
      addSample(/**/);
      i = i + 1;
    }
    render(tile);
}

render_stuff([
    {/*...*/}, 
    {/*...*/}
])
console.log("done rendering");

答案 1 :(得分:1)

问题是,在每次操作发生之前,对convertTileToRGB的所有调用都会开始对tile的引用进行操作。

如果你想让操作成为附加的,你必须让每个操作都对前一个操作起作用,但为了做到这一点,你必须等待前面的操作结束。

我不知道您实际执行了哪些操作,但也可能以“增量”非破坏性方式执行这些操作。

如果这不是一个选项,要控制一系列代码可读的回调,我建议使用一些流控制库,如async.js

答案 2 :(得分:1)

您可以做的是采取一系列步骤并将其组织为一个列表:

var thingsToDo = [
  { params: p1, process: function(pixels, params) {
    // ... first function ...
  }},
  { params: p2, process: function(pixels, params) {
    // ... second function ...
  }},
  { params: p3, process: function(pixels, params) {
    // ... third function ...
  }}
];

现在您可以创建一个函数来执行一系列转换:

function sequence( tile, thingsToDo ) {
  function doOneThing( ) {
    var thing = thingsToDo[thingCount];

    convertToRgb(tile, function(pixels) {
      thing.process(pixels, thing.params);
      thingCount ++;
      if (thingCount < thingsToDo.length)
         doOneThing();
    });
  }

  var thingCount = 0;
  doOneThing();
}

当你打电话给它时,它会迭代你的“事物”,一次做一个。我为你的回调添加了一个“params”参数;目前尚不清楚这些参数对象究竟是如何计算出代码中的情况的。因此,每个“东西”都是一个参数块和一个回调函数。

这样就可以像这样调用“sequence”函数:

sequence(tile, thingsToDo);

答案 3 :(得分:0)

如果您遇到异步执行问题,请尝试使用信号量进行同步。 http://en.wikipedia.org/wiki/Asynchronous_semaphore

在非线程安全操作完成之前,交替阻止调用。此外,如果方法似乎没有积累,请确保前一个方法的结果是您在下一个方法中用于操纵图像的逻辑的参数,而不是原始图像作为参数。

这个问题很模糊,所以我希望这会有所帮助。

答案 4 :(得分:0)

您可以查看JavaScript Promises。也许他们提供了一些有用的方法来解决你的问题。

答案 5 :(得分:0)

好的,所以我做了一个调度程序,你可以异步添加更多todo方法,用不同的签名调用,并且有一个实际上会逐个调用它们的setInterval。感谢https://stackoverflow.com/users/90511/missingnohttps://stackoverflow.com/users/182668/pointy我被赋予了一系列调用方法的想法。问题是我必须改变我的所有功能才能拥有相同的签名,并且有一个cronjob将检查调度员是否有更多的调用(Pointy有这个但是第一眼看不清楚)。如果他们没有相同的签名,你就无法真正相互离开(我想)。这就是为什么我没有选择missno的答案。我必须自己实施调度员,这就是为什么我不接受其他部分答案。这是我为我做的转换的例子。就像那样:

addSample = function(tile,sample,num_samples,which_sample){}
render = function(tile){}
...

并成为:

addSample = function(config,dispatcher){
     var tile = config["tile"];
     var sample = config["sample"];
     var num_samples = config["num_samples"];
     var which_sample = config["which_sample"];

     something.call(function(){
        //async part of code
        dispatcher.check(); // important! 
     });
}
render = function(config,dispatcher){
     var tile = config["tile"];
     something.call(function(){
        //async part of code
        dispatcher.check(); // important! 
     });
}
...

如果不调用dispatcher.check()作为最后一行,那么对于每个间隔(见下文),您将只有一个TODO调用。但我们需要所有剩下的东西。然后我有一个调度员:

var dispatcher = { };
dispatcher.current = 0;
dispatcher.total = 0 ;
dispatcher.next = function(){
    this.current++;
}
dispatcher.hasMore = function(){
    return dispatcher.current<dispatcher.total;
}
dispatcher.addNew = function(todo){
    this.total++;
    this[this.total] = todo;
    return this;
}
dispatcher.exec = function(){
    this[this.current].func(this[this.current].config,this);    
    delete this[this.current]; //deletes the TODO just invoked
}

dispatcher.check = function(){
    if (this.hasMore()){
        dispatcher.next();
        dispatcher.exec();
    }
}

然后我在调度程序中添加更多TODO作业,每个作业都有func:funcName和config,以及异步函数曾经拥有的所有输入。

dispatcher.addNew({func: addSample, config: {tile: tile,sample: sample, num_samples: 6, which_sample: 1}});
dispatcher.addNew({func: render,config: {tile: tile}});
dispatcher.addNew({func: addSample, config: {tile: tile,sample: sample2, num_samples: 6, which_sample: 2}});
dispatcher.addNew({func: render,config: {tile: tile}});
dispatcher.addNew({func: addSample, config: {tile: tile,sample: sample3, num_samples: 6, which_sample: 3}});
dispatcher.addNew({func: render,config: {tile: tile}});
dispatcher.addNew({func: addSample, config: {tile: tile,sample: sample4, num_samples: 6, which_sample: 4}});
dispatcher.addNew({func: render,config: {tile: tile}});
dispatcher.addNew({func: addSample, config: {tile: tile,sample: sample5, num_samples: 6, which_sample: 5}});

间隔检查然后逐个调用,背对背所有TODO的左边。如果一组TODO过了一会儿,setInterval将按顺序一次性选择该组。

setInterval(function(){dispatcher.check();},1000);

谢谢大家的时间!我希望有人能像我一样找到有用的东西。