javascript - 由于 Promise 拒绝,Promise.all 永远不会被触发

标签 javascript promise

我有两个 promise 。一个读取 sample.txt 文件,另一个读取 /books/ 文件夹中的所有文件。第二个 Promise 使用名为 readFiles 的函数,该函数获取目录名并使用它们来查看每个文件。当所有的 promise 都准备好后,then 中的代码应该运行:

const p1 = new Promise((resolve, reject) => {
  fs.readdir(__dirname + '/books/', (err, archives) => {
    // archives = [ 'archive1.txt', 'archive2.txt']
    readFiles(archives, result => {
      if (archives.length === result.length) resolve(result)
      else reject(result)
    })
  })
})

const p2 = new Promise((resolve, reject) => {
  fs.readFile('sample.txt', 'utf-8', (err, sample) => {
    resolve(sample)
  })
})

Promise.all([p1, p2]).then(values => {
  console.log('v:', values)
}).catch(reason => {
  console.log('reason:', reason)
})

function readFiles (archives, callback) {
  const result = []
  archives.forEach(archive => {
    fs.readFile(__dirname + '/books/' + archive, 'utf-8', (err, data) => {
      result.push(data)
      callback(result)
    })
  })
}

但是,Promise.all 总是被拒绝:

reason: [ 'archive 1\n' ]

我做错了什么?

最佳答案

Promise 是一次性设备。一旦它们被拒绝或解决,它们的状态就永远不会改变。考虑到这一点,readFiles()为它读取的每个文件调用它的回调,并且每次调用该回调时您都会拒绝或解决,但是您使用它的方式会检查:

if (archives.length === result.length)

第一个永远不会成立,然后你就拒绝了。一旦该 promise 被拒绝,其状态就无法改变。后续调用回调也会调用reject()然后最后一个会调用resolve() ,但状态早已设置,因此只有第一次调用 reject()resolve()实际上做任何事情。其他人则被简单地忽略。所以,p1总是会拒绝,因此Promise.all()使用 p1总是会拒绝。

您需要更改readFiles()要么只在完成所有文件时调用回调一次,要么将其更改为返回一个在读取所有文件时解析的 promise ,或者更改使用回调的方式,这样您就不会在第一次时拒绝它被调用。

<小时/>

一般来说,如果您要使用 Promise,那么您希望在最低级别进行 Promise,并在任何地方使用 Promise 的优势(特别是错误传播),而不是混合回调和 Promise。为此,我建议:

fs.readFileP = function(fname, encoding) {
    return new Promise(function(resolve, reject) {
        fs.readFile(fname, encoding, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function readFiles(archives, encoding, callback) {
    return Promise.all(archives.map(function(file) {
        return fs.readFileP(file, encoding);
    }));
}

或者,更深入、更有前途的fs.readdir()另外,你会得到这个:

// helper functions
fs.readdirP = function(dir) {
    return new Promise(function(resolve, reject) {
        fs.readdir(dir, function(err, files) {
            if (err) return reject(err);
            resolve(files);
        });
    });
}

fs.readFileP = function(fname, encoding) {
    return new Promise(function(resolve, reject) {
        fs.readFile(fname, encoding, function(err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

function readFiles(archives, encoding) {
    encoding = encoding || 'utf8';
    return Promise.all(archives.map(function(file) {
        return fs.readFileP(file, encoding);
    }));
}

// actual logic for your operation
const p1 = fs.readdirP(__dirname + '/books/').then(readFiles);
const p2 = fs.readFileP('sample.txt', 'utf-8');

Promise.all([p1, p2]).then(values => {
  console.log('v:', values);
}).catch(reason => {
  console.log('reason:', reason);
});
<小时/>

如果您使用 Bluebird promise library这使得一次性 Promise 整个模块变得很容易,并且有一些额外的功能来管理 Promise 流程控制,那么上面的代码就简化为:

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

const p1 = fs.readdirAsync(__dirname + '/books/').then(files => {
    return Promise.map(archives, file => {
        return fs.readFileAsync(file, 'utf8');
    });
});
const p2 = fs.readFileAsync('sample.txt', 'utf-8');

Promise.all([p1, p2]).then(values => {
  console.log('v:', values);
}).catch(reason => {
  console.log('reason:', reason);
});

在此代码块中,Promise.promisifyAll()一行代码创建 fs 上每个方法的 promise 版本模块带有Async他们的后缀。在这里,我们使用 fs.readFileAsync()fs.readdirAsync()所以我们可以使用 Promise 来完成所有事情。

关于javascript - 由于 Promise 拒绝,Promise.all 永远不会被触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40821198/

相关文章:

c# - 尝试在 Google 地球插件中创建组选择框

javascript - ASP.Net 中下拉框的数据绑定(bind)

javascript - 使用多个 .then 更正 JavaScript 的求值顺序

jquery - 有没有办法在循环中使用 jQuery 的 deferred/promise/when/then 功能来进行多个 ajax 调用?

javascript - CompletableFuture.get() 相当于 javascript Promise 吗?

javascript - 页面加载后后退按钮就会触发

javascript - 如果我的所有用户都有 AD 帐户,使用集成 Windows 身份验证来保护 .NET WebAPI 以供 JavaScript 客户端使用的风险是什么?

javascript - jQuery-File-Upload - 一种形式的多个输入文件

javascript - 通过一系列 promise (JS 继承/接口(interface))编译结果

javascript - 如何使用angularjs等待 promise 完成返回值