2017-01-28 14 views
0

Следующий код отвечает за чтение файлов. Мое требование заключается в том, как найти, были ли все файлы прочитаны, чтобы я мог вернуть или разрешить обещание от родительской функции (readmultifiles).Как определить, что все файлы были прочитаны и решить обещание

 $.when(readmultifiles(files)) 
       .then(function(){//all files uploaded})) 

Над кодом инициирует файл для чтения. Что можно сделать, чтобы после чтения всех файлов был выполнен обратный вызов или можно сделать возврат.

 function readmultifiles(files) {    
      // Read first file 
      setup_reader(files, 0); 
     } 


     function setup_reader(files, i) { 
      var file = files[i]; 
      var name = file.name; 
      var reader = new FileReader(); 
      reader.onload = function(e) { 
       readerLoaded(e, files, i, name); 
      }; 
      reader.readAsBinaryString(file); 
      // After reading, read the next file. 
     } 

     function readerLoaded(e, files, i, name) { 
      // get file content 
      var bin = e.target.result; 
      // do sth with text 


      // If there's a file left to load 
      if (i < files.length - 1) { 
       // Load the next file 
       setup_reader(files, i + 1); 
      } 
     } 
+2

Прочитайте [этот ответ о '$ .when()'] (http://stackoverflow.com/questions/25613624/q-promise-difference-between-when-and-then/25614136#25614136) и этот [Пробовал ' $ .when() 'и не повезло] (http://stackoverflow.com/questions/24549066/tried-when-and-then-no-lucky/24549220#24549220). Вы должны передать ему ряд обещаний, которые разрешаются, когда их основные операции асинхронного копирования завершаются. У него нет волшебных сил, чтобы делать все это самостоятельно. Все это контролирует группу обещаний. Если вы хотите его использовать, вы можете начать с того, что ваши операции async используют и разрешают обещания. – jfriend00

+0

Привет jFriend, Можете ли вы проверить мой ответ. –

ответ

2

Есть несколько вещей, чтобы рассмотреть в хорошем дизайне с использованием обещаний, что ваша реализация может поучиться:

  1. Создать обещание (называемое «обещание») с самого низкого уровня a синхронизация у вас есть. Затем вы можете использовать функции обещания для управления логическим потоком и распространения ошибок, и ваш код будет последовательно реализовываться с обещаниями. В этом случае это означает, что вы должны обещать readFile(). Он также делает readFile() более полезным в другом месте вашего проекта или в будущих проектах.
  2. Убедитесь, что вы всегда правильно распространяете ошибки. С асинхронным кодом, когда вы не используете обещания, может быть трудно правильно получить ошибки обратно к исходному вызывающему, особенно если асинхронная логика заканчивается сложной (с вложенными или последовательностными операциями).
  3. Подумайте, должны ли быть выполнены ваши асинхронные операции или они могут выполняться параллельно. Если одна операция не зависит от другой, и вы вряд ли перегружаете какую-либо услугу несколькими запросами, то запуск параллельных операций часто приведет к более быстрому результату.
  4. Возвращает обещания от асинхронных функций, поэтому вызывающие могут знать, когда все будет сделано, и получить доступ к результатам async.
  5. Не создавайте другое обещание вокруг существующего обещания без необходимости (считается одним из обещающих анти-шаблонов).
  6. Если вы используете jQuery-обещания, попробуйте использовать функции jQuery, совместимые со стандартом обещаний, чтобы вы не сталкивались с проблемами совместимости в будущем или путали будущих читателей вашего кода, которые с большей вероятностью будут знать, как работают стандартные обещания.

Учитывая все это, вот пять способов реализовать ваш код, используя стандартные обещания, используя обещания jQuery, а также с вашими последовательностями операций или запускаться параллельно и используя обещания Bluebird. Во всех случаях в конце вы получаете массив результатов в порядке.

Promisify readFile() используя стандартные обещания

Во-первых, давайте «promisify» ваша операция ReadFile, так что вы можете использовать обещание логики контролировать вещи.

function readFile(file) { 
    return new Promise(function(resolve, reject) { 
     var reader = new FileReader(); 
     reader.onload = function(e) { 
      resolve(e.target.result); 
     }; 
     reader.onerror = reader.onabort = reject; 
     reader.readAsBinaryString(file); 
    }); 
} 

Со стандартными обещаниями, все операции параллельно

Чтобы запустить все файловые операции параллельно и возвращает все результаты в порядке и использовать стандартные обещания, вы можете сделать это:

function readmultifiles(files) { 
    return Promise.all(files.map(readFile)); 
} 

// sample usage 
readmultifiles(arrayOfFiles).then(function(results) { 
    // all results in the results array here 
}); 

Со стандартными обещаниями, все операции в последовательности

Чтобы выполнить все операции с файлами в последовательности (что не похоже на то, что вам нужно сделать здесь, потому что все операции являются независимыми, даже если ваш исходный код их упорядочивал) и возвращать все результаты в порядке и использовать стандартные обещания, вы можете сделай это.

Это несколько стандартный шаблон дизайна для секвенирования использует .reduce() для последовательности через массив и цепи все операции вместе с тем они работают по одному вниз последовательности цепи:

function readmultifiles(files) { 
    var results = []; 
    files.reduce(function(p, file) { 
     return p.then(function() { 
      return readFile(file).then(function(data) { 
       // put this result into the results array 
       results.push(data); 
      }); 
     }); 
    }, Promise.resolve()).then(function() { 
     // make final resolved value be the results array 
     return results; 
    }); 
} 

// sample usage 
readmultifiles(arrayOfFiles).then(function(results) { 
    // all results in the results array here 
}); 

И вот как это будет выглядеть с помощью JQuery обещает

Promisify readFile() с помощью JQuery обещает:

function readFile(file) { 
    return new $.Deferred(function(def) { 
     var reader = new FileReader(); 
     reader.onload = function() { 
      def.resolve(e.target.result); 
     }; 
     reader.onerror = reader.onabort = def.reject; 
     reader.readAsBinaryString(file); 
    }).promise(); 
} 

Run в параллель с JQuery:

function readmultifiles(files) { 
    return $.when.apply($, files.map(readFile)); 
} 

// sample usage 
readmultifiles(arrayOfFiles).then(function() { 
    var results = Array.prototype.slice(arguments); 
    // all results in the results array here 
}); 

И работать в последовательности с JQuery

function readmultifiles(files) { 
    var results = []; 
    files.reduce(function(p, file) { 
     return p.then(function() { 
      return readFile(file).then(function(data) { 
       // put this result into the results array 
       results.push(data); 
      }); 
     }); 
    }, $.Deferred().resolve()).then(function() { 
     // make final resolved value be the results array 
     return results; 
    }); 
} 

// sample usage 
readmultifiles(arrayOfFiles).then(function(results) { 
    // all results in the results array here 
}); 

Bluebird реализация

И, для полноты картины, я 'Покажу вам, как это выглядит, используя немного more advanced promise library like Bluebird tha t имеет дополнительные возможности, которые здесь полезны. Параллельный код и реализация readFile() такие же, как для стандартных обещаний, но для последовательного осуществления, он может воспользоваться некоторыми встроенными Bluebird операций для работы последовательности асинхронной, и было бы просто состоять из:

function readmultifiles(files) { 
    return Promise.mapSeries(files, readFile); 
} 

// sample usage 
readmultifiles(arrayOfFiles).then(function(results) { 
    // all results in the results array here 
}); 
+0

Благодарим jFriend за подробное описание.Позвольте мне пройти через это и обновить. –

+0

Вау. Это было похоже на чтение Библии. Эти вещи новы для меня. Спасибо, что открыли дверь. –

0

Что делать, если изменить структуру кода для этого

$.when(readmultifiles(files)).then(
         function(status) { 
          alert(status + ", things are going well"); 
         }, 
         function(status) { 
          alert(status + ", you fail this time"); 
         }, 
         function(status) { 
          $("body").append(status); 
         } 
       ); 
     function readmultifiles(files) { 

      var dfrd = $.Deferred(); 
      // Read first file 
      setup_reader(files, 0); 

      function setup_reader(files, i) { 
       var file = files[i]; 
       var name = file.name; 
       var reader = new FileReader(); 
       reader.onload = function(e) { 
        readerLoaded(e, files, i, name); 
       }; 
       reader.readAsBinaryString(file); 
       // After reading, read the next file. 
      } 

      function readerLoaded(e, files, i, name) { 
       // get file content 
       var bin = e.target.result; 
       // do sth with text 

       namee.push(name); 
       // If there's a file left to load 
       if (i < files.length - 1) { 
        // Load the next file 
        setup_reader(files, i + 1); 
       } else { 
        dfrd.resolve(namee.join(',')); 
       } 
      } 
      return dfrd.promise(); 
     } 
+0

Здесь вообще не нужно использовать '$ .when()' здесь. Вы можете просто сделать readmultifiles (файлы). Then (...) '. – jfriend00

+0

, поскольку когда 'then' принимает три аргумента обратного вызова? –

+0

@JaromandaX - у jQuery есть третий аргумент для обратного вызова прогресса: https://api.jquery.com/deferred.then/. Очень нестандартный. Вероятно, не следует использовать, если вам нужна совместимость с остальной частью мира обещаний. – jfriend00