【问题标题】:Javascript Coding Challenge with setTimeout/Asynchronous Output带有 setTimeout/异步输出的 Javascript 编码挑战
【发布时间】:2023-06-23 06:51:01
【问题描述】:

我正在尝试解决以下挑战,我必须编写一个函数triggerActions,将回调传递给processAction,并产生输出:

"Process Action 1"
"Process Action 2"
...
"Process Action n"

这里是提供的功能:

function processAction(i, callback) {
  setTimeout(function() {
    callback("Processed Action " + i);
  }, Math.random()*1000);
}

函数编码:

function triggerActions(count) {

}

请注意,processAction 的代码不能更改。我正在考虑使用 Promise,但我不确定如何使用。我相信 setTimeout 实际上是同步的,所以我不知道 async/await 是否可以工作。

我的尝试:

triggerActions = count => {
    let promises = [];
    for(let i=1; i<=count; i++) {
    promises.push(new Promise( (resolve, reject) => processAction(i, str => resolve(str))));
    }
    let results = []
    promises.forEach( promise => Promise.resolve(promise).then( async res => results.push(await res)));
    return results;
}

【问题讨论】:

  • setTimeout 实际上是a同步的——你试过任何代码吗,你希望有人为你解决这个挑战吗?跨度>
  • promisify processAction,然后async/´await` 就可以了。
  • @JaromandaX 我刚刚用我的代码编辑了我的帖子。
  • 您希望它按顺序打印(即 1、2、3、4..n)还是它们的执行顺序(即随机顺序)?

标签: javascript promise async-await settimeout


【解决方案1】:

我有点喜欢短而甜:

var n = 5
var stop = 1

triggerActions = function(text) {
    if (text) console.log(text)
    if (stop <= n){
        processAction(stop++, triggerActions)
    }
}
triggerActions()

附言

我突然想到,也许您被允许提供一个函数,这意味着函数外部的stop 变量声明是一个问题。它使它有点冗长,但你可以将它全部包装在函数中,如下所示:

function triggerActions(stop) {
    var rFn = (text) => {
        if (text) console.log(text)
        if (stop <= n){
            processAction(stop++, rFn)
        }
    }
    rFn()
}
triggerActions(1)

【讨论】:

    【解决方案2】:

    你去吧:

    // Your unaltered function
    function processAction(i, callback) {
      setTimeout(function() {
        callback("Processed Action " + i);
      }, Math.random()*1000);
    }
    
    // The function you want to implement
    function triggerActions(count) {  
      var triggerAction = function (i) {    // Local function to process the given action number:
        if (i <= count) {                   // More actions to execute?
          processAction(i, function (text) {// Process current action number and pass a callback in parameter
            console.log(text);              // Write the result of processAction             
            triggerAction(i + 1);           // Trigger the next action 
          });                               //
        }                                   //
      }                                     
      triggerAction(1);                     // First things first: start at action one
    }
    
    // Call the function
    triggerActions(10);

    【讨论】:

    • 我建议将条件放在i++ 前面而不是在回调中,这样当count 为零时您的代码就不会失败。
    • @Bergi 谢谢你是对的。我更新了代码,并通过使用基于 1 的索引将其清除。
    【解决方案3】:

    原发帖人使用承诺的本能是正确的。

    上述两种解决方案可能有效,但由于每次调用 triggerActions() 都必须等待延迟过去,然后才能进行下一次调用,因此速度相当慢。

    也许这就是你想要的,但这里有一个使用 Promise 和 Promise.all() 的优化解决方案:

    const processAction = (i, callback) => {
      setTimeout(function() {
        callback("Processed Action " + i);
      }, Math.random()*1000);
    }
    
    const triggerActions = (n) => {
      const promises = [];
      const generatePromise = (i) => {
        return new Promise((resolve, reject) => {
          processAction(i, resolve);
        });
      }
      for (let i = 1; i <= n; i += 1) {
        promises.push(generatePromise(i));
      }
      Promise.all(promises)
        .then((strings) => strings.forEach((string) => console.log(string)));
    }
    
    triggerActions(10);
    

    要比较性能差异,请尝试并排运行这两种方法。

    【讨论】:

      【解决方案4】:

      这是我的解决方案:

      function processAction(i, callback) {
        setTimeout(function() {
         callback("Processed Action " + i);
        }, Math.random()*1000);
      }
      // Function to code:
      
      function triggerActions(count) {
        const asyncArr = [];
        for (let i = 1; i <= count; i++) {
          asyncArr.push(new Promise(resolve => processAction(i, resolve)));
        }
        Promise.all(asyncArr).then((vals) => {
          vals.forEach((val) => console.log(val))
        });
      }
      
      triggerActions(5);
      

      【讨论】:

        【解决方案5】:

        这是我使用 Promise.all 的解决方案:

        function triggerActions(count) {
          const promises = range(count).map(
            i => new Promise(resolve => processAction(i, resolve))
          );
        
          Promise.all(promises).then(results => {
            results.forEach(result => console.log(result));
          });
        }
        
        // Generates an array from 1...n
        function range(n) {
          return Array.from({ length: n }, (_, i) => i + 1);
        }
        

        【讨论】:

          【解决方案6】:

          要求函数“processAction”应保持不变并在批处理中调用。

          为此,我使用了 util.promisify 函数,它接受一个函数并将其转换为一个 Promise。可以使用 Promise.all 批量调用 Promise。

          另一个要求是回调应该输出“Processed Action i”,其中 i 是一个数字。已定义匿名函数“func”来执行此操作。

          triggerActions 函数接受一个数字 x,创建一个包含从 0 到 x 的索引的数字数组,然后同时调用 x 个异步函数的计数。

          const {promisify} = require('util');
          
          function processAction(i, callback) {
              setTimeout(function() {
                callback("Processed Action " + i);
              }, Math.random()*1000);
          }
          const func = (param1) => console.log(param1);
          const promisifyedProcessAction = promisify(processAction);
          
          
          async function triggerActions(count) {
              const arr = [];
              for(let i = 0; i < count;)
                  arr.push(++i);    
              await Promise.all(
                  arr.map((value) => promisifyedProcessAction(value,func)));
          }
          
          triggerActions(5);
          

          【讨论】:

          • 请避免只回答代码并提供一些关于其工作原理的解释。
          【解决方案7】:

          以下是所有可能方法的概述:

          基于回调:

          顺序:

          function triggerActions(count) {
            ;(function recur(i = 0) {
              processAction(i, (data) => {
                console.log(data)
                if (i < count) {
                  recur(i + 1)
                }
              })
            })()
          }
          

          并发

          function triggerActions(count) {
            const data = Array.from({ length: count })
            for (let i = 0; i < count; i++) {
              processAction(i, (result) => {
                data[i] = result
                count--
                if (count == 0) {
                  for (const x of data) {
                    console.log(x)
                  }
                }
              })
            }
          }
          

          基于承诺的:

          我们可以使用这个函数使processAction异步:

          function processActionP(i) {
            return new Promise((res) => processAction(i, res))
          }
          

          顺序:

          async function triggerActions(count) {
            for (let i = 0; i < count; i++) {
              const data = await processActionP(i)
              console.log(data)
            }
          }
          

          并发:

          async function triggerActions(count) {
            const data = await Promise.all(
              Array.from({ length: count }, (_, i) => processActionP(i)),
            )
            for (const x of data) {
              console.log(x)
            }
          }
          

          并发,使用 lodash/fp

          const _ = require('lodash/fp')
          
          const triggerActions = _.pipe(
            _.range(0),
            _.map(processActionP),
            Promise.all.bind(Promise),
            data => data.then(
              _.each(console.log)
            ),
          )
          

          【讨论】: