2012-11-27 5 views
1

Я попал в путаницу, которая, вероятно, связана с несколькими асинхронными ситуациями обратного вызова.несколько асинхронных обратных вызовов javascript, как это разрешить?

У меня есть яваскрипта функция называется populatePageArea()

Внутри populatePageArea он проходит через этот массив типа переменные, называемые страницы среди другого кода.

function populatePagesArea() { 
    // there was some code before the for loop 

    for (var i=0, l=pages.length; i<l; i++) {  
    addToPagesArea(pages[i], ""); 
    } 

    // some code after... 
} 

функция Внутри addToPagesArea я использовал FileAPI из HTML 5 для предварительного просмотра Drag'n упал файлов среди другого кода.

function addToPages(file, front) { 
    // there was some code before.. 
    reader = new FileReader(); 
    reader.onload = (function (theDiv) { 
    return function (evt) { 
     var backgroundimage = "url(" + evt.target.result + ")"; 
    theDiv.css("background-image", backgroundimage); 
     var sizeSettings = getSizeSettingsFromPage(file, calculateRatio); 

}; 

    }(imageDiv)); 

    // step#3 execute file reader 
    reader.readAsDataURL(file); 
    // there was some code after.. 
} 

Таким образом, каждый раз, когда я просматривал файл, я также делал некоторые вычисления по размерам файла.

function getSizeSettingsFromPage(file, whenReady) { 
    reader = new FileReader(); 
    reader.onload = function(evt) { 
     var image = new Image(); 
     image.onload = function(evt) { 
      var width = this.width; 
      var height = this.height; 
      var filename = file.name; 
      if (whenReady) { 
       whenReady(width, height, filename); 
      } 
     }; 
     image.src = evt.target.result; 
    }; 
    reader.readAsDataURL(file); 

} 
function calculateRatio(width, height, filename) { 

    var ratio = width/height; 

    var object = new Object(); 
    object['height'] = width; 
    object['width']  = height; 
    object['ratio']  = ratio; 
    object['size']  = 'Original'; 

    for (var size in SIZES) { 
     var min = SIZES[size].ratio - 0.01; 
     var max = SIZES[size].ratio + 0.01; 

     if (ratio <= max && ratio >= min) { 
      object['size'] = size; 
     } 
    } 

    pageSizes.add(filename, object); 

} 

pageSizes, как показано на calculateRatio представляет собой глобальную переменную, которая представляет собой массив-подобный тип переменной.

Он определенно пуст ПЕРЕД НАПОЛНЕНИЕМ.

Вот ситуация:

мой код:

populatePagesArea(); 
getMajorityPageSize(); // this acts on the supposedly non-empty pageSizes global variable 

Но потому, что я думаю, что calculateRatio не был вызван на ВСЕХ просматриваемо изображений, то pageSizes всегда пусто, когда getMajorityPageSize называется.

Как я могу убедиться, что после вызова populatePagesArea getMajorityPageSize запускается ТОЛЬКО после того, как все pages прошли функцию calculateRatio?

Я считаю, что это асинхронный обратный вызов. Но я не уверен, как это сделать для массива объектов, которые должны будут обработать функцию async callback, например calculateRatio.

ответ

1

Простое решение (я пометил свои изменения с // ***:

// *** 
var totalPages; 

function populatePagesArea() { 
    // there was some code before the for loop 

    // *** 
    totalPages = pages.length; 
    for (var i=0, l=pages.length; i<l; i++) {  
    addToPagesArea(pages[i], ""); 
    } 

    // some code after... 
} 

function addToPages(file, front) { 
    // there was some code before.. 
    reader = new FileReader(); 
    reader.onload = (function (theDiv) { 
     return function (evt) { 
     var backgroundimage = "url(" + evt.target.result + ")"; 
     theDiv.css("background-image", backgroundimage); 
     var sizeSettings = getSizeSettingsFromPage(file, calculateRatio); 
     // *** Check to see if we're done after every load 
     checkPagesReady(); 
     }; 

    }(imageDiv)); 

    // step#3 execute file reader 
    reader.readAsDataURL(file); 
    // there was some code after.. 
} 

// *** Call getMajorityPageSize() here, only after all pages have loaded. 
function checkPagesReady() { 
    if (pageSizes.length >= totalPages) 
     getMajorityPageSize(); 
} 

Лучшее решение, если вы собираетесь иметь дело с большим количеством асинхронных вещей позже будет реорганизовывать свой код, используя promises Promises - это API, разработанный для систематического и организованного асинхронного программирования, который упростит вашу жизнь, если вы собираетесь заниматься асинхронной работой. Существует множество бесплатных библиотек, которые поддерживают обещания, один из основных игроков - Q.js.

+0

Привет, Натан, спасибо! Я сделал только одно изменение кода. Я переместил вызов checkpagesready на последнюю строку calculateRatio. –

+1

, кстати, я столкнулся с этим https://github.com/caolan/async#series по сравнению с обещаниями, какой из них ... более перспективным? Хех, каламбур. –

+0

Если вы собираетесь много работать с Node, библиотека Async, с которой вы связаны, может быть очень полезна, потому что она более тесно связана с соглашениями узла. Тем не менее, я думаю, что обещания - более надежное и мощное решение, и были предприняты некоторые попытки переместить узел в этом направлении. Кроме того, есть много дискуссий о [добавлении обещаний к будущей версии ECMAScript] (http://wiki.ecmascript.org/doku.php?id=strawman:concurrency).Поэтому я определенно думаю, что они заслуживают изучения. –

Смежные вопросы