【问题标题】:How to use promises using nodejs?如何使用 nodejs 使用 Promise?
【发布时间】:2017-03-02 01:16:12
【问题描述】:

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

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);
    });

【问题讨论】:

  • 您在调用 readFile() 时忘记添加参数!
  • 我从客户端获得的那些参数,我如何通过它来保证它会在我启动应用程序时抛出未定义。
  • 就我个人而言,我发现 Promise 丑得要命。 Async.js 更易于阅读和理解。
  • 嗯我使用的是异步,但我无法解决这个问题,请参阅此处提出的问题stackoverflow.com/questions/42519121/…
  • @Mikey 在 nodebacks 中的错误处理比地狱还难看,async.js 中没有任何东西可以让我们摆脱它们。

标签: javascript node.js promise bluebird


【解决方案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:

在 Node 中承诺 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 => {...});

并承诺转换后的文件内容数组。

【讨论】:

  • rsp 库 - 为最终的承诺值做代理 - 是一个非常好的主意,但 imo 只会导致完全混乱,尤其是在新手中。它混合了Promise<A>A 的类型——文档甚至不清楚这是如何发生的。如果我的promise has a map methodrsp(resolve([])).map(…) 会调用数组还是 promise 方法呢?它肯定缺乏从代理返回“简单”承诺的方法。
  • 另外,rsp documentation 将自己与Promise constructor antipattern 进行比较——当然只有它才能获胜!正确的写作方式是var promise2 = promise1.then(s => s.toUpperCase()); var promise3 = promise2.then(s => s.split('').join('-')); - 省略then 并没有真正获得很多。
  • @Bergi 非常感谢您的 cmets。这是一个早期的想法,当然需要更好的文档。我考虑过在真实承诺与未来价值上调用方法的问题,并且有一些想法但尚未实现它们。现在它更像是一个实验,看看它是否有用。感谢您的建设性批评,我会考虑在未来尝试改进模块。
【解决方案2】:

你忘了调用“resolve”,也忘了传参数,而且好像把“results”当做全局变量,很糟糕。

这是我对代码的更正:

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);
    });

【讨论】:

  • 如果你没有在readFIle函数中传递回调,你是如何使用回调的
【解决方案3】:

错误 TypeError: expecting an array or an iterable object but got [object Null] 来自于没有将 logFiles 传递给您的 readFile() 调用,Promise.map 会抱怨它的第一个参数是 undefined

此外,您不能使用Promise.all(readFile(…)),因为Promise.all 确实接受了一组承诺而不是承诺(readFile 函数中的Promise.map 已经返回)。

但事实上,即使您修复了这些问题,您的代码仍然无法正常工作,因为您永远不会解决您创建的承诺。您实际上需要调用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);
});

【讨论】:

  • 所以我应该为 app.js 导出 findFileLines 并从那里调用它
  • @hussain 是的,试试看。
  • @Bergi 看到很多关于 Promise 的回答,我希望你能对 my answer 中的代码评论相同的问题。
猜你喜欢
  • 2018-01-21
  • 2018-09-09
  • 2018-04-07
  • 2021-05-09
  • 2018-03-09
  • 1970-01-01
  • 1970-01-01
  • 2017-05-03
  • 1970-01-01
相关资源
最近更新 更多