首页 文章

停止多次捕获的承诺链

提问于
浏览
4

在Node.js中,我需要读取一个文件并验证它的内容,所有这些都是异步的 . 我正在使用 Node.js 6.6bluebird 3.4.6

示例代码:

// pseudo function to read file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function readFile(flag) {
    return new Promise(function (resolve, reject) {
        console.log('Reading file...');
        if (flag) {
            resolve('File contents');
        } else {
            reject('readFile error');
        }
    });
}

// pseudo function to validate file contents - resolves when 'flag' is true, rejects when 'flag' is false.
function validate(fileContents, flag) {
    return new Promise(function (resolve, reject) {
        console.log('Validating file: ', fileContents);
        if (flag) {
            resolve('Validate passed');
        } else {
            reject('validation failed');
        }
    });
}


readFile(false)
    .then(function (fileContents) {
        console.log('Successfully read the file:', fileContents);
        return fileContents;
    })
    .catch(function (fileReadErr) {
        console.log('Failed to read the file:', fileReadErr);
        throw fileReadErr; // or, return Promise.reject(err);
    })
    .then(function (fileContents) {
        return validate(fileContents, false);
    })
    .then(function (result) {
        console.log('Successfully validated the file:', result);
    })
    .catch(function (err) {
        console.log('Failed to validate the file:', err);
    })
    ;
<script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script>

上面的代码将打印出来

Reading file...
Failed to read the file: readFile error
Failed to validate the file: readFile error

上述承诺链大致转换为以下同步代码:

try {
    let fileContents;

    try {
        fileContents = readFile(false);
        console.log('Successfully read the file:', fileContents);
    } catch (e) {
        console.log('Failed to read the file:', e);
        throw e;
    }

    let validationResult = validate(fileContents, false);
    console.log('Successfully validated the file:', validationResult);
} catch (err) {
    console.log('Failed to validate the file:', err);
}

并且,在第一个 catch 方法中投掷或拒绝 will still invoke the 2nd catch method .

My question :文件读取失败后有没有办法打破链?我的目标是从express.js路由返回不同的HTTP状态代码(文件读取错误:500,验证失败:400) .

我知道使用非标准专用 catch 方法的解决方案,但这需要特殊处理 . 从某种意义上说,我需要在错误对象中抛出错误或需要一些过滤密钥,而这两者都不在我手中,并且需要一些工作来实现它 . bluebird docs&here中提到了这个解决方案:Handling multiple catches in promise chain

4 回答

  • 1

    迄今为止最简单的解决方案是使用我称之为"insulated catches"的内容 . 即,每个 .catch() 是专家的模式,与整个过程中的特定步骤相关联,并且主链仅包括 . (并且最终仅包括单个终端捕获) .

    此外,在这种情况下,通过重新抛出具有附加属性的Error对象,可以在错误路径下传递添加的信息 . 这避免了对自定义错误的需要 .

    Promise.resolve()
    .then(function() {
        return readFile(false)
        .then(function (fileContents) {
            console.log('Successfully read the file:', fileContents);
            return fileContents;
        })
        .catch(function (error) {
            error.code = 521; // or whatever
            error.customMessage = 'Failed to read the file';
            throw error;
        })
    })
    .then(function (fileContents) {
        return validate(fileContents, false)
        .then(function (result) {
            console.log('Successfully validated the file:', result);
            return fileContents;
        })
        .catch(function (error) {
            error.code = 522; // or whatever
            error.customMessage = 'Failed to validate the file';
            throw error;
        });
    })
    .catch(function(error) { // terminal catch.
        console.log(error); 
        // It's possible for unaugmented errors to reach this point,
        // so be sure to test for the extra properties before trying to use them.
        if(error.code) {...}
        if(error.customMessage) {...}
        // Note also that the original error.message is still intact.
    });
    

    最初的 Promise.resolve() 并非绝对必要,但有助于保持其他所有内容的对称性 .

    这适用于任何Promises / A lib . 蓝鸟糖不是必需的 .

  • 4

    您可以创建自定义错误类型,如下所示:

    ReadFileError = function() {};
    ReadFileError.prototype = Error.prototype;
    
    ValidationError = function() {};
    ValidationError.prototype = Error.prototype;
    

    然后,你可以从承诺 throw 而不是拒绝:

    function validate(fileContents, flag) {
        return new Promise(function (resolve, reject) {
            console.log('Validating file: ', fileContents);
            if (flag) {
                resolve('Validate passed');
            } else {
                throw new ReadFileError('readFile error');
            }
        });
    }
    

    然后,您可以根据其类型捕获不同的错误:

    readFile(false)
        .then(function (fileContents) {
            console.log('Successfully read the file:', fileContents);
            return fileContents;
        })
        .then(function (fileContents) {
            return validate(fileContents, false);
        })
        .then(function (result) {
            console.log('Successfully validated the file:', result);
        })
        .catch(ReadFileError, function (err) {
            console.log(..., err);
        })
        .catch(ValidationError, function (err) {
            console.log(..., err);
        })
        catch(function(err) {
            ...
        });
    
  • 1

    也许更多的人可能会遇到同样的问题 . 我个人认为这不是最好的方法,因为那时你的app会抛出伪错误,这可能会被服务器上的其他错误处理错误地处理掉 . 但它像你提议的那样工作:

    // pseudo function to read file contents - resolves when 'flag' is true, rejects when 'flag' is false.
    function readFile(flag) {
        return new Promise(function (resolve, reject) {
            console.log('Reading file...');
            if (flag) {
                resolve('File contents');
            } else {
                throw new Error ('errorReading');
            }
        });
    }
    
    // pseudo function to validate file contents - resolves when 'flag' is true, rejects when 'flag' is false.
    function validate(fileContents, flag) {
        return new Promise(function (resolve, reject) {
            console.log('Validating file: ', fileContents);
            if (flag) {
                resolve('Validate passed');
            } else {
                throw new Error ('validationFailed');
    
            }
        });
    }
    
    readFile(false)
        .then(function (fileContents) {
            console.log('Successfully read the file:', fileContents);
            return fileContents;
        })    
        .then(function (fileContents) {
            return validate(fileContents, false);
        })
        .then(function (result) {
            console.log('Successfully validated the file:', result);
        })
        .catch((error) => {
            console.log(error.name);
            console.log(error.message);
            if (error.message === 'errorReading'){
                console.log('error 500 - File could\'d be read');
                // Maybe more custom error processing here
                //return res.status(500).send(JSON.stringify({
                //           'status' : 'error',
                //            'message' : 'File could\'d be read'
                //}));
            } else  if (error.message=== 'validationFailed'){
                console.log('error 500 - Validation not OK');
                // Maybe more custom error processing here            
                //return res.status(500).send(JSON.stringify({
                //            'status' : 'error',
                //            'message' : 'Validation not OK'
                //}));
            } else {
                console.log('error 500 - Some really bad stuff!');
                //return res.status(500).send(JSON.stringify({
                //            'status' : 'error',
                //            'message' : 'Some really bad stuff!',
                //            'errorMessage': error
                //}));
            }
        });
    
    <script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script>
    

    请注意,我注释掉了快递 res.send ,以避免处理此代码段时出错!

  • 0

    据我了解你想要实现的目标,我建议总是使用一个单一的catch块(什么时候可以避免在promise语句中引入嵌套,这在几个用例中完全没问题,但是应该避免它可以,因为你会可能最终带有承诺地狱与缩进

    你能否以统一的方式处理函数 readFilevalidate 中的所有错误:

    const error = new Error('something bad happened')
    error.status = 500
    return reject(error)
    

    然后你可以根据 status 在一个单一的catch块中处理错误逻辑,例如 res.status(err.status || 500).json(...)

相关问题