如何使用nodejs使用promises?

时间:2017-03-01 15:46:18

标签: javascript node.js promise bluebird

这是我第一次使用promises,所以我使用bluebird库来完成我的任务。在下面的代码中,我有来自客户端的字符串输入,我必须在fileSystem中搜索。所以我必须遍历dir并检查该字符串是否与任何文件相匹配,以便results行,所以一旦forEach完成,我想将results发送给客户端。如何使用promises实现该任务?以下代码错误

Error: Unhandled rejection TypeError: expecting an array or an iterable object but got [object Null]

app.js

   searchFileService.readFile(searchTxt, logFiles, function(lines, err) {
     console.log('Logs', lines);
     if (err)
         return res.send();
     res.json(lines);
 })

promises.js

function readFile(str,logFiles,callback){
   searchStr = str;
    return Promise.map(logFiles, function(logfile) {

        return new Promise(function(resolve, reject) {
             fs.readFile('logs/dit/' + logfile.filename, 'utf8', function(err, data) {
                               if (err) {
                                   callback(null,err);
                                   return; // **** Make sure not to run the code below!
                               }
                               var lines = data.split('\n'); // get the lines
                               lines.forEach(function(line) { // for each line in lines
                                   if (line.indexOf(searchStr) != -1) { // if the line contain the searchSt
                                       results.push(line);
                                   }
                               });
                            });
        })
    });
//}
}
Promise
    .all(readFile())
    .then(function() {
        console.log('done');
        callback(results);
    });

3 个答案:

答案 0 :(得分:1)

更新

我发布了一些一般性建议,然后发布了一些代码示例非常复杂的答案,所以我想使用正确的模块添加readFiles()函数可以非常紧凑:

function readFiles(str, files) {
  return Promise.all(files.map(
    file => rsp(fs.readFile(`logs/dit/${file}`, 'utf-8'))
       .split('\n').filter(l => l.includes(str))))
       .then(a => [].concat(...a));
}

这里正确的模块是mzrsp(免责声明,我是rsp模块的作者,因此我明显偏向于这里)。 mz模块允许您使用承诺版本的fs方法,而rsp模块允许您在将来调用承诺结果的方法。

您需要将这些模块用作:

let fs = require('mz/fs');
let rsp = require('rsp');

上面的readFiles()函数可以用作:

readFiles('line 3', ['file1.txt', 'file2.txt', 'file3.txt'])
  .then(lines => console.log(lines.join('\n')))
  .catch(err => console.log('Error:', err));

它确实有效 - 我用一堆文件测试了它logs/dit目录。

完整示例

let fs = require('mz/fs');
let rsp = require('rsp');

function readFiles(str, files) {
  return Promise.all(files.map(
    file => rsp(fs.readFile(`logs/dit/${file}`, 'utf-8'))
       .split('\n').filter(l => l.includes(str))))
       .then(a => [].concat(...a));
}

readFiles('line 3', ['file1.txt', 'file2.txt', 'file3.txt'])
  .then(lines => console.log(lines.join('\n')))
  .catch(err => console.log('Error:', err));

摘要

正如你所看到的,不需要循环,没有明确的回调,没有new Promise()构造等。

原始答案

如果要在Node中使用带有文件系统访问权限的Promises,请使用这些模块:

或者使用Bluebird的promisyfyAll:

宣传节点中的fs模块。

例如,使用mz,您可以执行以下操作:

return Promise.map(logFiles, file => fs.readFile('logs/dit/' + file, 'utf-8'));

在一行代码中返回所有这些文件内容的数组的承诺。

您可以进一步链接:

return Promise.map(...).then(files => ...);

获取所有文件并使用它们,或者您可以使用以下内容:

return Promise.map(logFiles, file => fs.readFile('logs/dit/' + file, 'utf-8').then(content => {...});

并且在转换后承诺提供一组文件内容。

答案 1 :(得分:0)

你忘了叫“解决”,你忘了传递参数,而你似乎正在使用“结果”作为全局变量,这很糟糕。

这是我对代码的更正:

function readFile(str,logFiles){
   searchStr = str;
    return Promise.map(logFiles, function(logfile) {

        return new Promise(function(resolve, reject) {
             fs.readFile('logs/dit/' + logfile.filename, 'utf8', function(err, data) {
                               if (err) {
                                   reject(err);
                                   return; // **** Make sure not to run the code below!
                               }
                               var results = [];
                               var lines = data.split('\n'); // get the lines
                               lines.forEach(function(line) { // for each line in lines
                                   if (line.indexOf(searchStr) != -1) { // if the line contain the searchSt
                                       results.push(line);
                                   }
                               });
                               resolve(results);
                            });
        })
    });
}

Promise
    .all(readFile(searchTxt, logFiles))
    .then(function(results) {
        console.log('done');
        callback(results);
    });

答案 2 :(得分:0)

错误 TypeError: expecting an array or an iterable object but got [object Null] 来自于logFiles readFile()来电Promise.mapundefined会对第一个参数{{1}感到不满}}

此外,您不能使用Promise.all(readFile(…)),因为Promise.all会承诺一系列承诺而非承诺(Promise.map函数中的readFile已经返回)。< / p>

但实际上,即使您修复了这些问题,您的代码仍然无效,因为您永远无法解决您创建的承诺。您需要实际调用rejectresolve,而不是某些callback

function readFile(filename) {
    return new Promise(function(resolve, reject) {
        fs.readFile(filename, 'utf8', function(err, data) {
            if (err) reject(err); // <--
            else resolve(data); // <--
        });
    });
}

现在您可以在循环中使用该功能:

function findFileLines(searchStr, logFiles) {
    return Promise.map(logFiles, function(logfile) {
        return readFile('logs/dit/' + logfile.filename).then(function(data) {
            var lines = data.split('\n'); // get the lines
            var result = lines.filter(function(line) {
                return line.indexOf(searchStr) != -1
            });
            return result;
        });
    })
}

findFileLines("…", [{filename:"…"}, …]).then(function(results) {
    console.log('done');
    callback(results);
});