最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - NodeJS Promise (Q) - How to get a value if the promise fails? - Stack Overflow

programmeradmin2浏览0评论

I´m starting to use promises in my NodeJS projects and I run into a problem. After I read the Promises/A+ spec and googling a lot I didn´t find a nice solution for the case that I need to access a value which gets produced in a promise chain. In my example I want to check when an error occurs if the image was created and if so, I want to delete it.

Code:

var Q = require('Q');
var fs = require('fs');

// This produces the imgPath
function makeImage() {
    var deferred = Q.defer();
    deferred.resolve("path/to/image");
    return deferred.promise;
}

function watermark(imgPath) {
    var deferred = Q.defer();
    deferred.resolve(imgPath);
    return deferred.promise;
}

// This function fails
function doSomeCoolThings(imgPath) {
    var deferred = Q.defer();
    deferred.reject(new Error("System is exploded"));
    return deferred.promise;
}

var fileExists = Q.denodeify(fs.readFile);
var deleteFile = Q.denodeify(fs.unlink);

// How do I get the imgPath here?
function deleteImageIfPresent(err) {
    return Q.fcall(function () {
        return imgPath !== undefined;
    })
        .then(fileExists)
        .then(deleteFile);
}

var iKnowThatSolution;

makeImage()
    // Thats not what I want
    //.then(function(imgPath) {
    //    iKnowThatSolution = imgPath;
    //})
    .then(watermark)
    .then(doSomeCoolThings)
    .fail(deleteImageIfPresent);

I´m starting to use promises in my NodeJS projects and I run into a problem. After I read the Promises/A+ spec and googling a lot I didn´t find a nice solution for the case that I need to access a value which gets produced in a promise chain. In my example I want to check when an error occurs if the image was created and if so, I want to delete it.

Code:

var Q = require('Q');
var fs = require('fs');

// This produces the imgPath
function makeImage() {
    var deferred = Q.defer();
    deferred.resolve("path/to/image");
    return deferred.promise;
}

function watermark(imgPath) {
    var deferred = Q.defer();
    deferred.resolve(imgPath);
    return deferred.promise;
}

// This function fails
function doSomeCoolThings(imgPath) {
    var deferred = Q.defer();
    deferred.reject(new Error("System is exploded"));
    return deferred.promise;
}

var fileExists = Q.denodeify(fs.readFile);
var deleteFile = Q.denodeify(fs.unlink);

// How do I get the imgPath here?
function deleteImageIfPresent(err) {
    return Q.fcall(function () {
        return imgPath !== undefined;
    })
        .then(fileExists)
        .then(deleteFile);
}

var iKnowThatSolution;

makeImage()
    // Thats not what I want
    //.then(function(imgPath) {
    //    iKnowThatSolution = imgPath;
    //})
    .then(watermark)
    .then(doSomeCoolThings)
    .fail(deleteImageIfPresent);
Share Improve this question asked Jan 14, 2014 at 18:33 stahlstiftstahlstift 451 silver badge5 bronze badges 2
  • I think it might be simplest to save the imagePath somewhere, or call makeImage again inside the fail block. – Frances McMullin Commented Jan 14, 2014 at 18:39
  • makeImage is a very expensive method - so that´s not possible. I am looking exactly for a different solution than saving in a temporary variable =) – stahlstift Commented Jan 14, 2014 at 18:41
Add a ment  | 

2 Answers 2

Reset to default 4

I remend this approach:

return makeImage().then(function (path) {
    return doSomeCoolThings(path)
    .finally(function () {
        return removeImage(path);
    });
});

Assuming you trust makeImage, removeImage, and doSomeCoolThings to return Q promises and never throw. Otherwise, there’s always Q.fcall which eliminates these concerns.

If you wish to retain the image in the success case, and only delete it if there’s a failure, rethrowing the error:

return Q.fcall(makeImage).then(function (path) {
    return Q.fcall(doSomeCoolThings, path)
    .catch(function (error) {
        return Q.fcall(removeImage, path)
        .thenReject(error);
    });
});

Also, instead of:

var deferred = Q.defer();
deferred.resolve(x);
return deferred.promise;

You can:

return Q.resolve(x);

Instead of:

deferred.reject(new Error("System is exploded"));

You could return an object containing both the error and additional data that you need in the error handler, like so:

deferred.reject({
  error: new Error("System is exploded"),
  data: {
    imgPath: imgPath,
    ...
  }
});

Then you can access imgPath with err.data.imgPath inside deleteImageIfPresent function.

You will probably want to handle errors created in 3rd party libraries. You can catch them in one error handler and re-throw the error with an explicit reject wrapping in an object and adding the data you need.


Also as a side note, there is no .fail in Promises/A+. Use:

promise.then(onFulfilled, onRejected);

Not technically an error but since you mentioned it.

发布评论

评论列表(0)

  1. 暂无评论