2016-01-06 2 views
15

Я пытаюсь понять, почему настройки ниже обещания не работают.Использование обещаний с fs.readFile в цикле

(Примечание: Я уже решил эту проблему с async.map Но я хотел бы узнать, почему мои попытки ниже не работает.).

Правильное поведение должно быть: bFunc должен работать столько времени, сколько необходимо, чтобы fs читал все файлы изображений (bFunc ниже запускается дважды), а затем консоль cFunc печатает «Конец».

Спасибо!

Попытка 1: Она запускается и останавливается в cFunc().

var fs = require('fs'); 

bFunc(0) 
.then(function(){ cFunc() }) //cFunc() doesn't run 

function bFunc(i){ 
    return new Promise(function(resolve,reject){ 

     var imgPath = __dirname + "/image1" + i + ".png"; 

     fs.readFile(imgPath, function(err, imagebuffer){ 

      if (err) throw err; 
      console.log(i) 

      if (i<1) { 
       i++; 
       return bFunc(i); 
      } else { 
       resolve(); 
      }; 

     }); 

    }) 
} 

function cFunc(){ 
    console.log("End"); 
} 

Попытка 2: В этом случае я использовал для цикла, но он выполняет из строя. Консольные отпечатки: Конец, bFunc done, bFunc done

var fs = require('fs'); 

bFunc() 
     .then(function(){ cFunc() }) 

function bFunc(){ 
    return new Promise(function(resolve,reject){ 

     function read(filepath) { 
      fs.readFile(filepath, function(err, imagebuffer){ 
       if (err) throw err; 
       console.log("bFunc done") 
      }); 
     } 

     for (var i=0; i<2; i++){ 
      var imgPath = __dirname + "/image1" + i + ".png"; 
      read(imgPath); 
     }; 

     resolve() 
    }); 
} 


function cFunc(){ 
    console.log("End"); 
} 

Спасибо за помощь заранее!

+0

В попытке 1, где вы обработчик ошибок для 'bFunc'? Если ошибка выдается, вы никогда не узнаете об этом с вашим текущим кодом. – nils

+0

Для чего этот код? Просьба описать проблему, которую вы пытаетесь решить словами, а не просто показывать нам код, который не делает то, что вы хотите. В коде есть много чего-то неправильного, поэтому я лучше начну понимать проблему, которую нужно решить, чем пытаться переделать все, что не так с вашим кодом, не зная конечной цели. – jfriend00

+0

@nils не вызывает ошибку при bFunc! Но я думаю, я должен положить улов в конце цепи. – David

ответ

41

Итак, в любое время, когда у вас есть несколько операций async, чтобы координировать в некотором роде, я сразу же хочу пойти на обещания. И лучший способ использовать обещания для координации нескольких асинхронных операций - заставить каждую асинхронную операцию вернуть обещание. Операция асинхронного действия самого низкого уровня, которую вы показываете, - fs.readFile(). Поскольку я использую библиотеку обещаний Bluebird, у нее есть функция для «обобщения» функций асинхронного целого модуля.

var Promise = require('bluebird'); 
var fs = Promise.promisifyAll(require('fs')); 

Это позволит создать новые параллельные методы на fs объекта с суффиксом «асинхронным», возвращающие обещания вместо использования прямых обратных вызовов. Таким образом, будет fs.readFileAsync(), который возвращает обещание. Вы можете больше узнать о пролонгации Bluebird here.

Итак, теперь вы можете сделать функцию, которая получает изображение довольно просто и возвращает обещание, значение которого данные с картинки:

function getImage(index) { 
    var imgPath = __dirname + "/image1" + index + ".png"; 
    return fs.readFileAsync(imgPath); 
} 

Затем, в вашем коде, это выглядит, как вы хотите make bFunc() - функция, которая считывает три из этих изображений и вызывает cFunc(), когда они будут выполнены.Вы можете сделать это так:

var Promise = require('bluebird'); 
var fs = Promise.promisifyAll(require('fs')); 

function getImage(index) { 
    var imgPath = __dirname + "/image1" + index + ".png"; 
    return fs.readFileAsync(imgPath); 
} 

function getAllImages() { 
    var promises = []; 
    // load all images in parallel 
    for (var i = 0; i <= 2; i++) { 
     promises.push(getImage(i)); 
    } 
    // return promise that is resolved when all images are done loading 
    return Promise.all(promises); 
} 

getAllImages().then(function(imageArray) { 
    // you have an array of image data in imageArray 
}, function(err) { 
    // an error occurred 
}); 

Если вы не хотите использовать Bluebird, вы можете вручную сделать версию обещание fs.readFile() так:

// make promise version of fs.readFile() 
fs.readFileAsync = function(filename) { 
    return new Promise(function(resolve, reject) { 
     fs.readFile(filename, function(err, data){ 
      if (err) 
       reject(err); 
      else 
       resolve(data); 
     }); 
    }); 
}; 

Или, в современных версиях узла. JS, вы можете использовать util.promisify() сделать promisified версии функции, которая следует за node.js асинхронного вызывающему конвенции:

const util = require('util'); 
fs.readFileAsync = util.promisify(fs.readFile); 

Хотя, вы быстро обнаружите, что когда-то у ou начните использовать обещания, вы хотите использовать их для всех асинхронных операций, чтобы вы «многократно просили» много вещей и имели библиотеку или, по крайней мере, общую функцию, которая сделает это для вас, сэкономит много времени.

+0

спасибо @ jfriend00, что очень помогает! – David

+0

Обратите внимание, что теперь вам нужно использовать 'Promise.promisifyAll' вместо' Promise.promisify' –

+0

. Обратите внимание: узел 8 поставляется с собственной функцией promisify: '' 'const {promisify} = require (" util "); const fs = require ("fs"); const readFile = promisify (fs.readFile); '' ' – Daniel

18

Ваш код должен выглядеть следующим образом:

var fs = require('fs'); 
var __dirname = "foo"; 

// promisify fs.readFile() 
fs.readFileAsync = function (filename) { 
    return new Promise(function (resolve, reject) { 
     try { 
      fs.readFile(filename, function(err, buffer){ 
       if (err) reject(err); else resolve(buffer); 
      }); 
     } catch (err) { 
      reject(err); 
     } 
    }); 
}; 

// utility function 
function getImageAsync(i) { 
    return fs.readFileAsync(__dirname + "/image1" + i + ".png"); 
} 

Использование с одного изображения:

getImageAsync(0).then(function (imgBuffer){ 
    console.log(imgBuffer); 
}).catch(function (err) { 
    console.error(err); 
}); 

Использование с несколькими изображениями:

var images = [1,2,3,4].map(getImageAsync); 
Promise.all(images).then(function (imgBuffers) { 
    // all images have loaded 
}).catch(function (err) { 
    console.error(err); 
}); 

Для promisify функция означает использовать асинхронную функцию с семантикой обратного вызова a nd выводит из нее новую функцию с семантикой обещания.

Это может быть сделано вручную, как показано выше, или - предпочтительно - автоматически. Среди прочего, библиотека обещаний Bluebird имеет помощника для этого, см. http://bluebirdjs.com/docs/api/promisification.html

+0

Спасибо !!!! =) – David

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