Managing promise dependencies

后端 未结 3 613
暖寄归人
暖寄归人 2021-01-04 13:39

I\'m using Node.js and Bluebird to create some fairly complicated logic involving uncompressing a structured file, parsing JSON, creating and making changes to several Mongo

相关标签:
3条回答
  • 2021-01-04 13:46

    It is certainly not an antipattern to return promises within thens, flattening nested promises is a feature of the promise spec.

    Here's a possible rewrite, though I'm not sure it's cleaner:

    var doStuff = function () {
    
      promise1()
      .then(function (value1) {
    
        return promise2()
        .then(function (value2) {
    
          return promise3(value1)
          .then(successFunction)
          .finally(function() {
            cleanup(null, value1, value2);
          });
    
        })
        .finally(function() {
          cleanup(null, value1, null);
        });
    
      })
      .finally(function () {
        cleanup(null, null, null);
      });
    
    };
    

    Or another option, with atomic cleanup functions:

    var doStuff = function () {
    
      promise1()
      .then(function (value1) {
    
        return promise2()
        .then(function (value2) {
    
          return promise3(value1)
          .then(successFunction)
          .finally(function() {
            cleanup3(value2);
          });
    
        })
        .finally(function() {
          cleanup2(value1);
        });
    
      })
      .finally(function (err) {
        cleanup1(err);
      });
    
    };
    

    Really, I feel like there's not much you can do to clean this up. Event with vanilla try/catches, the best possible pattern is pretty similar to these.

    0 讨论(0)
  • 2021-01-04 13:48

    I find both answers currently provided nice but clumsy. They're both good but contain overhead I don't think you need to have. If you instead use promises as proxies you get a lot of things for free.

    var doStuff = function () {
      var p1 = promise1();
      var p2 = p1.then(promise2);
      var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do.
      return Promise.all([p1, p2, p3]).catch(function(err){
          // clean up based on err and state, can unwrap promises here
      });
    };
    

    Please do not use successFunction and such it is an anti-pattern and loses information. If you feel like you have to use successFunction you can write:

    var doStuff = function () {
      var p1 = promise1();
      var p2 = p1.then(promise2);
      var p3 = p1.then(promise3); // if you actually need to wait for p2 here, do.
      Promise.join(p1, p2, p3, successFunction).catch(function(err){
          // clean up based on err and state, can unwrap promises here
      });
    };
    

    However, it is infinitely worse since it won't let the consumer handle errors they may be able to handle.

    0 讨论(0)
  • 2021-01-04 13:48

    This question might be more appropriate for code review but here is how I'd approach it given this example:

    var doStuff = function () {
      // Set up your promises based on their dependencies. In your example
      // promise2 does not use dependency1 so I left them unrelated.
      var dep1Promise = promise1();
      var dep2Promise = promise2();
      var dep3Promise = dependency1Promise.then(function(value){
        return promise3(value);
      });
    
      // Wait for all the promises the either succeed or error.
      allResolved([dep1Promise, dep2Promise, dep3Promise])
          .spread(function(dep1, dep2, dep3){
    
        var err = dep1.error || dep2.error || dep3.error;
        if (err){
          // If any errored, call the function you prescribed
          cleanupDependingOnSystemState(err, dep1.value, dep2.value);
        } else {
          // Call the success handler.
          successFunction(dep3.value);
        }
    };
    
    // Promise.all by default just fails on the first error, but since
    // you want to pass any partial results to cleanupDependingOnSystemState,
    // I added this helper.
    function allResolved(promises){
      return Promise.all(promises.map(function(promise){
        return promise.then(function(value){
          return {value: value};
        }, function(err){
          return {error: err};
        });
      });
    }
    

    The use of allResolved is only because of your callback specifics, if you had a more general error handler, you could simply resolve using Promise.all directly, or even:

    var doStuff = function () {
      // Set up your promises based on their dependencies. In your example
      // promise2 does not use dependency1 so I left them unrelated.
      var dep1Promise = promise1();
      var dep2Promise = promise2();
      var dep3Promise = dependency1Promise.then(function(value){
        return promise3(value);
      });
    
      dep3Promise.then(successFunction, cleanupDependingOnSystemState);
    };
    
    0 讨论(0)
提交回复
热议问题