2016-12-23 2 views
1

Недавно я столкнулся с проблемой при выполнении файла node.js. Я отправлю код и объясню, в чем проблема.Почему Promise возвращает (разрешать) пустой объект в Node.js?

У меня есть 2 файла, а именно testit.js и test.js

В test.js, я передаю объект массив, содержащий файл пути текстовых файлов, к testit.js с помощью module.exports

["a.txt","b.txt"] 

В testit.js, module.exports.text принимает объект массив имен файлов,
обрабатывает каждый из него через Object.keys(texts).forEach,
считывает каждый из возвращаемых значений буфера через readFile,
возвращает текст, содержащий в этом буфере, через takeAction
и сохраняет его в объекте массива newtexts.

Но когда newtexts разрешен и вызов возвращается в то(), в котором newtexts печатается на командной строке, он возвращает EMPTY ARRAY ОБЪЕКТ, а не возвращающий массив объектов содержимого файла каждого этих файлов.

Может кто-нибудь объяснить мне, где я ошибся в коде? Спасибо за тонну.

test.js

var testit = require('./testit'); 
var texts = ["a.txt","b.txt"]; 

testit.text(texts).then(function(newtexts){ 
    console.log(newtexts); 
}); 

testit.js

var Promise = require('bluebird'); 
var S = require('string'); 
var fs = require("fs"); 

module.exports.text = function(text){ 

    var texts = text; 
    var length = Object.keys(texts).length; 

    return new Promise(function(resolve, reject){ 
     var newtexts = []; 

     var takeAction = function(text) { 
      return text.toString(); 
     } 

     var readFile = function (filename, enc){ 
       return new Promise(function (resolve, reject){ 

        fs.readFile(filename, enc, function (err, buffer){ 
          if(err) 
          reject(err); 
          else 
          resolve(buffer); 
        }); 
       }); 
     } 

     Object.keys(texts).forEach(function(key){ 

      readFile(texts[key]).then(function(text){ 
       newtexts[key] = takeAction(text); 
      }); 
     });   

     resolve(newtexts); 
    }); 
} 
+0

Поскольку вы вызываете решение (newtexts), прежде чем что-либо фактически добавлено в newtexts. Ни один из callback-запросов forEach() даже не начал выполняться по точке, к которой вы достигли линии разрешения. Используйте 'array.map' для сопоставления массива ваших текстов с массивом Promises и используйте' Promise.all(). Then() ', чтобы дождаться, пока все обещания в массиве не будут выполнены до разрешения Promise. – PMV

+0

Я новичок в Node.js, поэтому можете ли вы отправить решение на это, если это возможно? –

+0

Не ответ, но я нахожу довольно странным, что вы используете Object.keys в массиве и получаете доступ к массиву [key]. Почему бы просто не зациклиться на самом массиве? – Gerrit0

ответ

1

Вы должны фактически ждать все ReadFile Обещает решить, прежде чем разрешить общий посыл.

Заменить

Object.keys(texts).forEach(function(key){ 

     readFile(texts[key]).then(function(text){ 
      newtexts[key] = takeAction(text); 
     }); 
    });   

    resolve(newtexts); 

что-то вроде этого:

var textPromises = texts.map(function (fileName) { 
    return readFile(fileName).then(function (text) { 
     newtexts[fileName] = takeAction(text); 
    }); 
}); 

Promise.all(textPromises).then(function() { 
    resolve(newtexts); 
}); 

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

0

@pmv Теперь, когда вы упомянули о Promise.all и .map, все это имеет смысл сейчас.Для того, чтобы получить лучшее понимание в этом я remodified код на takeAction, чтобы вернуть Promise:

var takeAction = function (text) { 
    return new Promise(function (resolve,reject){ 
     resolve(text.toString()); 
    }); 
} 

, а также внутри textPromises только после того, как readFile возвращается:

var textPromises = texts.map(function (fileName) { 
    return readFile(fileName).then(function (buffer) { 
     //newtexts[fileName] = takeAction(text); 
     return takeAction(buffer).then(function (text){ 
      newtexts[text] = text; 
     }); 
    }); 
}); 

Вот скриншот результата: SS of the result

Большое вам спасибо за помощь :)

0

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

  • Наружная обертка new Promise() ненужно, потому что вы можете вернуть обещание, возвращенное Promise.all(promises).... Это не только избавит вас от ненужного обещания, но также позволит распространять ошибки вызывающему. Обратите внимание, что в вашем собственном коде внешние вызовы reject никогда не вызываются.
  • Возврат нового обещания от takeAction() снизит эффективность дважды; во-первых, нужно создать обещание, а во-вторых, нужно другое .then() (следовательно, еще одно обещание) получить доступ к результату. Если операции синхронны, попробуйте сохранить их синхронно.
  • .then() s в цикле .map() можно избежать полностью, переключив синхронную обработку buffer на readFile() (соответствующим образом переименованный). Опять же, синхронные синхронные операции.

Попробуйте это:

module.exports.text = function (fileNames) { 
    var newtexts = {}; // <<<<<<< Object not Array. 

    function takeAction(key, buffer) { // <<<<<<< takeAction now accepts key and buffer 
     newtexts[key] = buffer.toString(); // <<<<<<< make the assignment here 
    } 

    function readFileAndTakeAction(key) { 
     return new Promise(function (resolve, reject) { 
      fs.readFile(filenames[key], null, function (err, buffer) { 
       if(err) { 
        reject(err); 
       } else { 
        takeAction(key, buffer); // <<<<<<< by doing this here, you avoid an extra .then() elsewhere. 
        resolve(); 
       } 
      }); 
     }); 
    } 
    var promises = Object.keys(fileNames).map(readFileAndTakeAction); 

    // Instead of resolving an outer promise, return Promise.all(...).then(...) 
    return Promise.all(promises).then(function() { 
     return newtexts; 
    }); 
} 

Изменение readFile() от общего назначения promisifier из fs.readFile() в специалиста, возможно, грабит код некоторой элегантности, но, безусловно, обеспечивает большую эффективность. Кроме того, элегантность .map(readFileAndTakeAction) более чем компенсирует.

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