停止承诺链有多个陷阱

man*_*nta 7 node.js promise bluebird

在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);
    })
    ;
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdn.jsdelivr.net/bluebird/3.4.6/bluebird.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

上面的代码将打印

Reading file...
Failed to read the file: readFile error
Failed to validate the file: readFile error
Run Code Online (Sandbox Code Playgroud)

上面的承诺链大致翻译为下面的同步代码:

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);
}
Run Code Online (Sandbox Code Playgroud)

并且,在第catch一种方法中抛出或拒绝仍将调用第二种catch方法

我的问题:一旦文件读取失败,是否有任何方法可以打破这个链条?我的目标是从express.js路由返回不同的HTTP状态代码(文件读取错误:500,验证失败:400)。

我知道使用非标准专用catch方法的解决方案,但是需要特殊处理。从某种意义上说,我需要在错误对象中抛出错误或需要一些过滤键,而这两者都不在我的手中,并且需要一些工作来实现它。蓝鸟文档和此处都提到了此解决方案:处理Promise链中的多个捕获

Roa*_*888 5

到目前为止,最简单的解决方案是使用我所谓的“绝缘渔获物”。即,一种模式,其中每个人.catch()都是专家,与整个过程中的特定步骤相关联,并且主链仅包含thens(最终是单个最终捕获)。

同样,在这种情况下,通过重新抛出具有附加属性的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.
});
Run Code Online (Sandbox Code Playgroud)

最初Promise.resolve()不是严格必需的,但是可以使其他所有内容保持对称。

这将与任何Promises / A +库一起使用。不需要蓝鸟糖。


mar*_*ful 3

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

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

ValidationError = function() {};
ValidationError.prototype = Error.prototype;
Run Code Online (Sandbox Code Playgroud)

然后,您可以throw从 Promise 而不是拒绝:

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');
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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) {
        ...
    });
Run Code Online (Sandbox Code Playgroud)