【问题标题】:How to replace Promise.defer with new Promise()如何用新的 Promise() 替换 Promise.defer
【发布时间】:2016-04-30 11:48:26
【问题描述】:

所以我看到 Promise.defer 现在已弃用,我们现在应该使用 new Promise 代替。但是对于此示例,我看不到如何执行此操作?

var Promise = require('bluebird');
var interval;

var rollDice = function (resolver) {
  console.log("rolling");
  if (Math.floor(Math.random() * 10) == 7) {
    clearInterval(interval);
    resolver.resolve();
  }
}

var rollTill7 = function (ms) {
    var resolver = Promise.defer();
    interval = setInterval(function(){rollDice(resolver);},ms);
    return resolver.promise;
}

rollTill7(100).then(function(){
    console.log("rolled a 7");
});

【问题讨论】:

    标签: node.js promise


    【解决方案1】:

    一般来说,建议放弃旧的 defer 模型,因为您希望 Promise 的创建者负责解决或拒绝它 - 这只会使控制流更容易遵循。您不想将解决或拒绝的责任转嫁给其他代码。

    如果解决或拒绝的决定涉及外部代码(例如您的rollDice() 函数),则它可以返回用于解决或拒绝的信息。例如,在您的代码示例中,可以这样做。

    请注意,rollDice() 函数现在只是一个掷骰子函数,它告诉您是否掷出特定数量的非。然后由其他函数使用它来确定控制流,而不是将控制流放在掷骰子函数本身中。

    var rollDice = function() {
      console.log("rolling");
      return Math.floor(Math.random() * 10) + 1;
    }
    
    var rollTillNum = function(num, ms) {
        return new Promise(function(resolve) {
            var interval = setInterval(function(){
                if (rollDice() === num) {
                   resolve();
                   clearInterval(interval);
                }
            }, ms);
        });
    }
    
    rollTillNum(7, 100).then(function(){
        console.log("rolled a 7");
    });
    

    变更摘要:

    1. Promise 管理是自包含的,没有委托给其他函数(使代码逻辑更易于遵循),这是使用新 Promise 构造函数而不是延迟构造函数的主要原因之一。
    2. interval 变量现在包含在本地范围内。
    3. rollDice() 函数现在是通用的,因此可以在其他上下文中使用。
    4. rollDice() 现在返回一个从 1 开始的值,而不是一个从 0 开始的值(因为骰子就是这样工作的)。
    5. 而不是硬连线 rollTill7(),现在是 rollTillNum(),您将其传递给您希望它达到的数字。

    虽然上述解决方案更通用(使用外部函数提供有关是否应该解决的反馈),但在这种特定情况下,如果您甚至不需要 rollDice() 函数在外部可用,那么它可以完全包含在 rollTillNum() 函数中:

    var rollTillNum = function(num, ms) {
        return new Promise(function(resolve) {
            var interval = setInterval(function(){
                if ((Math.floor(Math.random() * 10) + 1) === num) {
                   resolve();
                   clearInterval(interval);
                }
            }, ms);
        });
    }
    
    rollTillNum(7, 100).then(function(){
        console.log("rolled a 7");
    });
    

    以下是将上述代码制成一个工作演示:

    document.getElementById("roll").addEventListener("click", function() {
        var start = Date.now();
        rollTillNum(7, 100).then(function(cnt) {
            var elapsed = ((Date.now() - start) / 1000).toFixed(1);
            log("It took " + elapsed + " seconds and " + cnt + " rolls to roll a 7");
        });
    });
    
    var rollDice = function() {
      console.log("rolling");
      return Math.floor(Math.random() * 10) + 1;
    }
    
    var rollTillNum = function(num, ms) {
        return new Promise(function(resolve) {
            var cntr = 0;
            var interval = setInterval(function(){
                ++cntr;
                if (rollDice() === num) {
                   resolve(cntr);
                   clearInterval(interval);
                }
            }, ms);
        });
    }
    
    function log(x) {
        var div = document.createElement("div");
        div.innerHTML = x;
        document.body.appendChild(div);
    }
    <button id="roll">
    Roll a 7
    </button><br><br>

    【讨论】:

      【解决方案2】:

      使用new Promise(constructorFn) 的直接等效方法是将interval = setInterval(...) 写入constructorFn 并将constructorFn 的resolve 参数传递给rollDice(),而不是原来的Deferred 对象。

      var Promise = require('bluebird');
      var interval;
      
      var rollDice = function (resolve) {
        console.log("rolling");
        if (Math.floor(Math.random() * 10) == 7) {
          clearInterval(interval);
          resolve();
        }
      };
      
      var rollTill7 = function (ms) {
          return new Promise(function(resolve, reject) {
              interval = setInterval(function() {
                  rollDice(resolve);
              }, ms);
          });
      };
      
      rollTill7(100).then(function() {
          console.log("rolled a 7");
      });
      

      如果intervalrollDice() 也被移动到promise 的构造函数中,解决方案会变得更加简洁。不仅从外部命名空间中删除了两个成员,而且还避免了传递 resolve 的需要 - 由于关闭,rollDice() 可以访问它。

      var Promise = require('bluebird');
      
      var rollTill7 = function (ms) {
          return new Promise(function(resolve, reject) {
              var interval;
              var rollDice = function () {
                console.log("rolling");
                if (Math.floor(Math.random() * 10) == 7) {
                  clearInterval(interval);
                  resolve();
                }
              };
              interval = setInterval(rollDice, ms);
          });
      };
      
      rollTill7(100).then(function() {
          console.log("rolled a 7");
      });
      

      再迈出一小步,但可以说是重要的一步,您可能会选择将rollDice() 作为匿名函数移动到setInterval 表达式中。这将回避命名函数的问题,它不仅仅是“掷骰子” - 它还可以解决。

      【讨论】:

      • 接受这个作为答案,因为我很欣赏改进的范围界定解决方案。
      • @GroovyDotCom - 我不认为这些解决方案中的任何一个都是理想的。第一个仍然将解决 Promise 的责任委托给外部函数,这根本不是必需的,并且确实会使某人理解和遵循代码的能力变得复杂,尤其是在更大的代码体中。首先不使用 deferred 的全部意义在于将解决和拒绝责任保持在本地,而不是将其转移到其他地方。第二个例子很方便,但是它隐藏了rollDice()函数,所以不能在别处使用
      • @GroovyDotCom - 虽然在这个特定示例中隐藏 rollDice() 函数可能没问题,但更一般的情况是该函数在外部声明,因为它在其他地方使用(否则,它不会t 甚至需要成为一个函数)。您可以在此处查看其他答案之一以获得更通用的解决方案。
      • @jfriend00,我同意这一切。为了演示“如何用新的 Promise() 替换 Promise.defer”的原则,我决定尽可能地坚持 OP 的原始方法。实际上,我会按照您的建议做很多事情。
      • 感谢漫游者。艰难的决定,但经过编辑,我认为 Jfriend 现在有了完整的答案。
      【解决方案3】:

      试试这个

      var rollDice = function (resolve) {
        console.log("rolling");
        if (Math.floor(Math.random() * 10) == 7) {
          clearInterval(interval);
          resolve();
        }
      }
      
      var rollTill7 = function (ms) {
          return new Promise(function(resolve) {
              interval = setInterval(function() {
                  rollDice(resolve);
              }, ms);
          });
      }
      

      【讨论】:

      • rollDice() 函数确实没有理由必须知道某个地方的承诺,因此真的没有理由在承诺构造函数之外委托解决承诺。 rollDice() 的生活工作是随机掷骰。 rollTill7() 中的代码可以使用来自rollDice() 的返回值,然后rollTill7() 可以确定是否已达到所需的数量以及是否应解决承诺。如果 Promise 是在同一个代码块中创建和解析的,那么它们最容易理解。
      • @jfriend00,你是对的。我只是将defer 替换为new Promise。但是,您的建议是更好的做法...
      • 因此,解决方案仅针对此问题。如果我们使用简单的 Promise 和 resolve 和 reject 而不是 Promise.deferred 如下 - return new Promise((resolve, reject) => { resolve() reject() }
      猜你喜欢
      • 2018-07-13
      • 2017-12-08
      • 1970-01-01
      • 2020-12-18
      • 2016-12-18
      • 1970-01-01
      • 2016-12-26
      • 2020-09-20
      • 2017-10-09
      相关资源
      最近更新 更多