1

作为我的另一个问题的“分支”问题(参见编辑 4)如何在不使用 canvas getImageData 的情况下将 png base64 字符串转换为像素数组?

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

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: 尝试更改我的代码:调度程序如下所示。每次调用的磁贴和样本可能不同,但对于某些调用可能相同。将应用除 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);

使用该代码,不会保留顺序,并且不会为下一次调用保留图块上发生的情况。

4

6 回答 6

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");
于 2013-06-23T19:26:30.693 回答
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(tile, thingsToDo);
于 2013-06-23T19:38:27.010 回答
1

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

如果您希望操作具有附加性,则必须使每个操作都对前一个操作的结果起作用,但要做到这一点,您必须等待前一个操作结束。

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

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

于 2013-06-23T19:28:46.547 回答
0

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

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

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

于 2013-06-23T19:29:03.203 回答
0

好的,所以我创建了一个调度程序,您可以异步添加更多要使用不同签名调用的 todo 方法,并且有一个 setInterval 将实际调用它们。感谢https://stackoverflow.com/users/90511/missingnohttps://stackoverflow.com/users/182668/pointy我被赋予了调用一系列方法的想法。问题是我必须更改我所有的函数以具有相同的签名并有一个 cronjob 来检查调度程序是否有更多的调用(Pointy 有,但乍一看并不清楚)。如果他们没有相同的签名,你就不能真正从另一个开始(我认为)。这就是为什么我没有选择missingno的答案。我必须自己实现调度程序,这就是我不接受其他部分答案的原因。这是我为我所做的转换示例。就像这样:

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);

谢谢大家的时间!我希望有人会像我一样发现这很有用。

于 2013-06-24T00:28:28.267 回答
0

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

于 2013-06-23T20:00:43.447 回答