简体   繁体   English

带有 setTimeout/异步输出的 Javascript 编码挑战

[英]Javascript Coding Challenge with setTimeout/Asynchronous Output

I am trying solve the following challenge where I have to write a function triggerActions that passes a callback into the processAction , and produces the output:我正在尝试解决以下挑战,我必须编写一个函数triggerActions将回调传递给processAction并生成输出:

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

Here is the provided function:这是提供的功能:

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

Function to code:代码功能:

function triggerActions(count) {

}

Note that the code for processAction cannot be altered.请注意,无法更改processAction的代码。 I was thinking of using a Promise but I'm not sure how.我正在考虑使用 Promise,但不确定如何使用。 I believe the setTimeout is actually synchronous so I don't know if async/await would work.我相信 setTimeout 实际上是同步的,所以我不知道 async/await 是否有效。

My attempt:我的尝试:

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;
}

I kind of like short and sweet:我有点喜欢简短而甜美:

var n = 5
var stop = 1

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

PS聚苯乙烯

It occurred to me that perhaps you are only allowed to provide a function which means the stop variable declaration outside the function is a problem.我突然想到,也许你允许提供一个函数,这意味着函数外的stop变量声明是一个问题。 It makes it a little more verbose, but you can wrap it all inside the function like this:它使它有点冗长,但你可以将它全部包装在函数中,如下所示:

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

There you go:给你:

 // 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);

The original poster's instinct to use promises was correct.原始海报使用承诺的直觉是正确的。

The two solutions above may work but because each call to triggerActions() has to wait for the delay to elapse before the next call can be made, this is considerably slow.上面的两个解决方案可能有效,但由于每次调用 triggerActions() 都必须等待延迟结束才能进行下一次调用,因此速度相当慢。

Maybe this is what you want but here's an optimized solution using promises and Promise.all():也许这就是您想要的,但这是使用 promises 和 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);

To compare the performance differences, try running the two approaches side by side.要比较性能差异,请尝试并排运行这两种方法。

Here's my solution:这是我的解决方案:

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);

Here is my solution using Promise.all:这是我使用 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);
}

The requirements are that the function 'processAction' should remain unchanged and invoked in a batch.要求是函数“processAction”应保持不变并批量调用。

For this I have used the util.promisify function that takes a function and converts it into a promise.为此,我使用了 util.promisify 函数,它接受一个函数并将其转换为一个 promise。 A promise can be invoked in a batch with Promise.all.可以使用 Promise.all 批量调用一个 promise。

Another requirement is that the callback should output “Processed Action i” where i is a number.另一个要求是回调应该输出“Processed Action i”,其中 i 是一个数字。 The anonymous function 'func' has been defined to do this.已定义匿名函数“func”来执行此操作。

The triggerActions function takes a number, x, creates an array of numbers containing indices from 0 to x and then invokes a count of x asynchronous functions simultaneously. 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);

Here's an overview of all the possible approaches:以下是所有可能方法的概述:

Callback-based:基于回调:

Sequential:顺序:

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

Concurrent同时

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)
        }
      }
    })
  }
}

Promise-based:基于承诺:

We can use this function to make processAction async:我们可以使用此函数使processAction异步:

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

Sequential:顺序:

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

Concurrent:同时:

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

Concurrent, using lodash/fp并发,使用lodash/fp

const _ = require('lodash/fp')

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM