【问题标题】:Dynamic Chaining in Javascript PromisesJavascript Promise 中的动态链接
【发布时间】:2018-07-13 12:32:42
【问题描述】:

如何在 Javascript Promises 中执行动态链接,我一直只看到调用的硬编码,例如(promise).then(request/functionName).then(request/functionName)

【问题讨论】:

  • 动态的——它们来自哪里?您要解决的问题是什么?当然,你可以传递你喜欢的任何回调,无论选择如何,不仅仅是硬编码的函数表达式。
  • 感谢 Bergi 的回复,它们来自用户操作/事件,例如按钮点击等,
  • 你的意思是像var promise = …; button.onclick = function(e) { promise.then(doSomething); };?当然,你可以做到这一点,是的。你试过了吗?
  • 是的,以同样的方式..我用完了 Ideas Bergi......
  • 那么你想要得到的结果是什么?您是否已经有无法运行或看起来太复杂的代码?

标签: javascript promise


【解决方案1】:

给定一个所有返回 promise 的数组函数,您可以使用 reduce() 顺序运行它们:

var myAsyncFuncs = [
    function (val) {return Promise.resolve(val + 1);},
    function (val) {return Promise.resolve(val + 2);},
    function (val) {return Promise.resolve(val + 3);},
];

myAsyncFuncs.reduce(function (prev, curr) {
    return prev.then(curr);
}, Promise.resolve(1))
.then(function (result) {
    console.log('RESULT is ' + result);  // prints "RESULT is 7"
});

上面的例子使用了ES6 Promises,但是所有的promise库都有类似的特性。

此外,创建承诺返回函数的数组通常是使用map() 的良好候选者。例如:

myNewOrmModels.map(function (model) {
    return model.save.bind(model);
}).reduce(function (prev, curr) {
    return prev.then(curr);
}, Promise.resolve())
.then(function (result) {
    console.log('DONE saving');
});

【讨论】:

  • 这是最简洁的答案
  • 有什么想法可以避免reduce造成的内存浪费?
  • @Салман 你可以在 for 循环中做同样的事情。创建一个包含承诺的变量(例如var myPromise = Promise.resolve()),然后循环遍历您的函数数组并在每一步中重新设置您的承诺变量(例如myPromise = myPromise.then(...))。如果你不重置你的 promise 变量,你所有的异步函数都将并行运行,而不是单步运行。
  • 对不起,我当时不清楚。我的意思是由于创建了如此多的承诺而造成的内存浪费。例如,我有 100 件事情要按顺序做,我会做 reduce/for 和链接 100 个 promise。我创建了 100 个承诺并消耗了所有的内存。如果说我的第二个承诺被拒绝,那么将不再有用,并且控制直接去捕获。这就是我所说的优化内存使用的意思。我想知道我们是否只能创建我们接下来要执行的承诺。
  • 还没有测试,但是看起来很整洁!
【解决方案2】:

一种选择是利用对象的属性以及通过字符串调用它们的能力。

我写了一个小样本Here并贴在下面。

我们的想法是,您在某个命名空间或对象中设置了您希望运行的一组函数,就像我在“myNamespace”中所做的那样:

myNamespace = {
    "A": function() {return "A Function";},
    "B": function() {return "B Function";},
    "C": function() {return "C Function";}
}

然后你的主要承诺会运行,并且以某种方式(通过输入、ajax、提示等)你会得到你想要运行的函数的字符串值,直到运行时才知道:

我的主要承诺使用提示来获取用户的来信:

var answer = prompt('Starting.  Please pick a letter: A,B,C');
        if(myNamespace[answer] === undefined)
        {
            alert("Invalid choice!");
            reject("Invalid choice of: " + answer);
        }
        else
        {
            resolve(answer);
        }

在下一个“then”中,我使用该值(通过 resolve 函数传递)来调用该函数:

.then(function(response) {
        funcToRun = myNamespace[response]();})

最后,我将动态函数调用的结果输出到 html 中,并使用一些递归的乐趣使其更具交互性并证明它是动态的:

.then(function(){
        document.getElementById('result').innerHTML = funcToRun;})
    .then(function(){
        if(prompt("Run Again? (YES/NO)")==="YES")
        {
            doWork();
        }
    });

myNamespace = {
    "A": function() {return "A Function";},
    "B": function() {return "B Function";},
    "C": function() {return "C Function";}
}

function doWork()
{
    var funcToRun;
    
    new Promise(function(resolve,reject) {
        var answer = prompt('Starting.  Please pick a letter: A,B,C');
        if(myNamespace[answer] === undefined)
        {
            alert("Invalid choice!");
            reject("Invalid choice of: " + answer);
        }
        else
        {
            resolve(answer);
        }
    })
    .then(function(response) {
        funcToRun = myNamespace[response]();})
    .then(function(){
        document.getElementById('result').innerHTML = funcToRun;})
    .then(function(){
        if(prompt("Run Again? (YES/NO)")==="YES")
        {
            doWork();
        }
    });
}

doWork();
<div id="result"></div>

【讨论】:

  • 谢谢你,杰森...我会尝试实现相同的,让你知道
  • 嗨 Jason....快速问题....我会从用户操作中收到很多请求(要调用的 fns),如何检查当前的承诺状态,如果是,则链接如果状态已解决,仍待处理或直接运行
  • @RamuAjay 所以,有一个 Promise 状态值,但它是私有的。我的猜测是使用.done(function() {isPromiseDone = true;}) 或一些外部变量来保存当前的承诺状态。至于添加额外的“thens”,您可以保存一个函数数组,在.done() 函数中,您可以检查该数组并将更多“thens”附加到promise ......但这只是一个猜测。
【解决方案3】:

由于 promises 解包,只要继续添加 then 语句,它将继续链接在一起

function asyncSeries(fns) {
  return fns.reduce(function(p, fn) {
    return p.then(fn);
  }, Promise.resolve());
}

递归也是一种很酷的方法:)

function countTo(n, sleepTime) {
  return _count(1);

  function _count(current) {
    if (current > n) {
      return Promise.resolve();
    }

    return new Promise(function(resolve, reject) {
      console.info(current);
      setTimeout(function() {
        resolve(_count(current + 1));
      }, sleepTime);
    });
  }
}

【讨论】:

  • 嗨 Travis....快速问题....我会从用户操作中收到很多请求(要调用的 fns),我如何检查当前的承诺状态,然后链接到它如果如果承诺状态已解决(已解决或已拒绝),它仍处于挂起状态或直接运行下一个函数 -
  • 您将函数的递归调用包装在 resolve() 中是最终得到我一直在努力工作的代码的原因。非常感谢递归版本。
【解决方案4】:

这是 ES7 的方式。

假设您在一个数组中定义了多个 Promise。

  var funcs = [
    _ => new Promise(res => setTimeout(_ => res("1"), 1000)),
    _ => new Promise(res => setTimeout(_ => res("2"), 1000))
  }

你想这样打电话。

 chainPromises(funcs).then(result => console.log(result));

您可以为此使用asyncawait

  async function chainPromises(promises) {
    for (let promise of promises) {  // must be for (.. of ..)
      await promise();
    }
  }

这将按顺序(一个接一个)执行给定的函数,而不是并行执行。参数promises是一个函数数组,返回Promise

Plunker:http://plnkr.co/edit/UP0rhD?p=preview

【讨论】:

  • 09/12/2019 这是最简单的方法。谢谢。
【解决方案5】:

我认为最简单的方法是:

const executePromises = function(listOfProviders){

    const p = Promise.resolve(null);

    for(let i = 0; i < listOfProviders.length; i++){
       p = p.then(v => listOfProviders[i]());
    }

   return p;

};

我相信上面基本上相当于:

const executePromises = async function(listOfProviders) {

    for(let i = 0; i < listOfProviders.length; i++){
       await listOfProviders[i]();
    }

};

【讨论】:

    【解决方案6】:

    此解决方案基于 EcmaScript 6 (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) 中引入的使用承诺,因此在使用前请查看表格浏览器的支持https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility

    代码

    var f1 = function(){
        for (var i = 0; i < 800000000; i++) {}
        console.log('Function1 is done');
    }
    var f2 = function(){
        for (var i = 0; i < 800000000; i++) {}
        console.log('Function2 is done');
    }
    var f3 = function(){
        for (var i = 0; i < 800000000; i++) {}
        console.log('Function3 is done');
    }
    var f4 = function(){
        for (var i = 0; i < 800000000; i++) {}
        console.log('Function4 is done');
    }
    
    
    callbacks = function(){
    
        // copy passed arguments
        var callbacks = arguments;
    
        // create array functions
        var callbacks = Object.keys(callbacks).map(function(el){ return callbacks[el] });
    
        var now = Date.now();
    
        callbacks.reduce(function(previousPromise, currentFunc){
            return previousPromise.then(
                function(){
                    currentFunc();
                    var seconds = (Date.now() - now) / 1000;
                    console.log('Gone', seconds, 'seconds');
                }
            )
        }, Promise.resolve());
    }
    
    callbacks(f1, f2, f3, f4);
    

    Chrome 控制台中的结果(值秒会有所不同):

    Function1 is done
    Gone 1.147 seconds
    Function2 is done
    Gone 2.249 seconds
    Function3 is done
    Gone 3.35 seconds
    Function4 is done
    Gone 4.47 seconds
    

    注意事项:

    1. 如果函数包含计时器则不起作用(针对此问题 我也尝试了 jQuery 的 $Callbacks、$.Ajax 和 $.When 但它没有帮助。 我发现的唯一决定是在 a 的回调中使用 resolve() 计时器,但如果您已完成功能,则不可接受。)。
    2. 测试环境

    $ google-chrome --version
    Google Chrome 53.0.2785.116
    

    【讨论】:

      【解决方案7】:

      我的 api 提供者刚刚遇到了一个问题,即执行 Promise.all() 会导致并发数据库问题..

      处理我的情况是,我需要获得每个承诺结果,以便显示一些“一切正常”或“有些错误”警报。

      而且我不知道为什么 .. 当 promise 解决时使用 reduce 的这段小代码我无法让我的范围工作(现在调查太晚了)

      $scope.processArray = function(array) {
          var results = [];
          return array.reduce(function(p, i) {
              return p.then(function() {
                  return i.then(function(data) {
                      results.push(data);
                      return results;
                  })
              });
          }, Promise.resolve());
      }
      

      感谢http://hellote.com/dynamic-promise-chains/这个帖子,我带着这个小混蛋来了。它没有经过打磨,但工作正常。

      $scope.recurse = function(promises, promisesLength, results) {
      
          if (promisesLength === 1) {
              return promises[0].then(function(data){
                  results.push(data);
                  return results;
              });
          }
      
          return promises[promisesLength-1].then(function(data) {
              results.push(data);
              return $scope.recurse(promises, promisesLength - 1, results);
          });
      
      }
      

      然后我像这样调用函数:

      var recurseFunction = $scope.recurse(promises, promises.length, results);
      recurseFunction.then(function (response) { ... });
      

      希望对你有帮助。

      【讨论】:

        【解决方案8】:

        查看以下教程

        1. javascript/node.js 承诺的编程(动态)链接和
        2. 使用递归函数的承诺链

        Programmatic-Chaining-and-Recursive-Functions-with-JavaScript-Promise

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-01-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多