使用 Promises 清理意大利面条代码

Cleaning up Spaghetti Code w/ Promises

在我的一个 beforeSave 函数中,我需要在响应成功或错误之前检查一些条件。

问题是,我的代码看起来真的很乱,有时 success/error 没有被调用:

Parse.Cloud.beforeSave("Entry", function(request, response) {
    var entry = request.object;
    var contest = request.object.get("contest");

    entry.get("user").fetch().then(function(fetchedUser) {
      contest.fetch().then(function(fetchedContest) {
            if ( fetchedUser.get("fundsAvailable") < fetchedContest.get("entryFee") ) {
              response.error('Insufficient Funds.');
            } else {
                fetchedContest.get("timeSlot").fetch().then(function(fetchedTimeSlot) {
                    var now = new Date();
                    if (fetchedTimeSlot.get("startDate") < now) {
                        response.error('This contest has already started.');
                    } else {
                        contest.increment("entriesCount");
                        contest.increment("entriesLimit", 0); //have to do this, otherwise entriesLimit is undefined in save callback (?)

                        contest.save().then(function(fetchedContest) {
                            if (contest.get("entriesCount") > contest.get("entriesLimit")) {
                                response.error('The contest is full.');
                            } else {
                                response.success();
                            }
                        });
                    }
                });
            }
        });
    });
});

我一直在尝试学习 promises 来整理它,这是我的(失败的)尝试:

Parse.Cloud.beforeSave("Entry", function(request, response) {
    var entry = request.object;
    var contest = request.object.get("contest");

    entry.get("user").fetch().then(function(fetchedUser) {
        contest.fetch().then(function(fetchedContest) {
            if ( fetchedUser.get("fundsAvailable") < fetchedContest.get("entryFee") ) {
              response.error('Insufficient Funds.');
            }
            return fetchedContest;
        });
    }).then(function(result) {
        result.get("timeSlot").fetch().then(function(fetchedTimeSlot) {
            var now = new Date();

            if (fetchedTimeSlot.get("startDate") < now) {
                response.error('This contest has already started.');
            }
        });
    }).then(function() {
        contest.increment("entriesCount");
        contest.increment("entriesLimit", 0);
        contest.save().then(function(fetchedContest) {
            if (contest.get("entriesCount") > contest.get("entriesLimit")) {
                response.error('The contest is full.');
            }
        });
    }).then(function() {
        response.success();
    }, function(error) {
        response.error(error);
    });
});

任何关于如何为此使用承诺的帮助或示例将不胜感激。显然我还没有完全理解它们在语法上是如何工作的。

你正在犯菜鸟错误,即在链接它们时不返回 .then 内的承诺,除此之外,即使你调用 response.error(..,你的链仍然会继续,打破链的最简单方法是抛出错误,您的代码可以展平为:

Parse.Cloud.beforeSave("Entry", (request, response) => {
  var entry = request.object,
   , contest = request.object.get("contest")
   , fetchedUser
   , fetchedContest;

  entry.get("user").fetch()
    .then(_fetchedUser => {
      fetchedUser = _fetchedUser;
      return contest.fetch();
    }).then(_fetchedContest => {
      fetchedContest = _fetchedContest;
      if ( fetchedUser.get("fundsAvailable") < fetchedContest.get("entryFee") ) 
        return Promise.reject('Insufficient Funds.');      
      return fetchedContest.get("timeSlot").fetch();
    }).then(fetchedTimeSlot => {
      var now = new Date();
      if (fetchedTimeSlot.get("startDate") < now) 
        return Promise.reject('This contest has already started.');      
      contest.increment("entriesCount");
      contest.increment("entriesLimit", 0); //have to do this, otherwise entriesLimit is undefined in save callback (?)
      return contest.save();
    }).then(fetchedContest => {
      if (contest.get("entriesCount") > contest.get("entriesLimit")) 
        return Promise.reject('The contest is full.');          
      response.success();          
    }).catch(response.error.bind(response));

});

我通过使用承诺链首先组装获取的变量来稍微清理一下。这遵循了一些样式规则,使其更具可读性(无论如何对我来说)...

Parse.Cloud.beforeSave("Entry", function(request, response) {
    var entry = request.object;
    var contest = request.object.get("contest");

    var fetchedUser, fetchedContest;
    var errorMessage;

    entry.get("user").fetch().then(function(result) {
        fetchedUser = result;
        return contest.fetch();
    }).then(function(result) {
        fetchedContest = result;
        return fetchedContest.get("timeSlot").fetch();
    }).then(function(fetchedTimeSlot) {
        // now we have all the variables we need to determine validity
        var now = new Date();
        var hasSufficientFunds = fetchedUser.get("fundsAvailable") >= fetchedContest.get("entryFee");
        var contestNotStarted = fetchedTimeSlot.get("startDate") >= now;
        if (hasSufficientFunds && contestNotStarted) {
            contest.increment("entriesCount");
            contest.increment("entriesLimit", 0); //have to do this, otherwise entriesLimit is undefined in save callback (?)
            return contest.save();
        } else {
            errorMessage = (hasSufficientFunds)? 'This contest has already started.' : 'Insufficient Funds.';
            return null;
        }
    }).then(function(result) {
        if (!result) {
            response.error(errorMessage);
        } else {
            if (contest.get("entriesCount") > contest.get("entriesLimit")) {
                response.error('The contest is full.');
            } else {
                response.success();
            }
        }
    }, function(error) {
        response.error(error);
    });
});

请注意我们如何从不让 response.anything() 悬而未决,我们总是通过 return.

明确什么流向下一个承诺。