【问题标题】:How can I simultaneously run 3 instances of a function如何同时运行一个函数的 3 个实例
【发布时间】:2020-04-24 22:28:56
【问题描述】:

我有一个名为scan() 的函数。它被循环调用。但是,它目前的工作方式:

  1. 致电scan()
  2. 等待scan()返回
  3. 增加min
  4. 重复

我怎样才能让它同时运行scan()2 实例。我可以将2 实例更改为3 实例或45 等。

示例: 我希望它是怎样的(在下面的示例中,scan()3 实例在任何一个时间点都在运行。它不应该超过 3扫描运行的实例(相对于 while 循环条件)。

调用scan() 并在后台等待返回值。一旦它得到一个返回值,它就会增加min 并调用scan() 的另一个实例。总是有 3scan() 实例在运行(相对于 while 循环条件)

<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<div class="display-error" style="display: none"></div>
<form>
  <label for="fname">Fruit (only correct input is: banana)</label><br>
  <input type="text" id="fruit-name" name="fruit" value="banana"><br>
  <button type="submit" id="submit" value="Submit">Submit</button>
</form>

<div id="results">
</div>

<script type="text/javascript">
$(document).ready(function() {


    $('#submit').click(function(e) {
        e.preventDefault();

        var fruitName = $("#fruit-name").val();

        $.ajax({
            type: "POST",
            url: "verify-input.php",
            dataType: "json",
            data: {
                fruitName: fruitName
            },
            success: function(data) {
                if (data.code == 200) {
                    $("#submit").html("Running Scan");
                    (async function() {
                        var fruitID = data.fruitId;
                        var min = 1;
                        while (min < 1000) {
                            await scan(fruitID, min, min + 30);
                            min = min + 30;
                        }
                    })();
                } else {
                    $(".display-error").html("<ul>" + data.msg + "</ul>");
                    $(".display-error").css("display", "block");
                }
            }
        });


    });
});

function scan(vFruitId, min, max) {

    return $.ajax({
        type: "POST",
        url: "scanner.php",
        dataType: "json",
        data: {
            vFruitId: vFruitId,
            min: min,
            max: max
        },
        success: function(data) {
            data.forEach((item, idx) => {
                $("#results").append(`
                <div class="fruit-item" data-item="${idx}">
                    <div class="f-calories">calories: ${item.sweetness}</div>
                    <div class="f-sweetness">sweeteness: ${item.calories}</div>
                    <div class="f-bitterness">bitterness: ${item.bitterness}</div>
                </div><br>
              `);
            })
        }
    });

}

</script>

【问题讨论】:

  • 这是因为你的扫描函数没有返回任何承诺
  • @MisterJojo , $.ajax 确实返回了一个承诺。问题是这些请求是同步运行的,而不是同时运行的。

标签: javascript html jquery while-loop async-await


【解决方案1】:

这是您要同时运行的代码。

//...
while (min < 1000) {
  await scan(fruitID, min, min + 30);
  min = min + 30;
}
//...

目前正在同步运行。

有一个库可以抽象出限制并发操作的一些复杂性,它被称为promise-throttle

实现看起来类似于:

const promiseThrottle = new PromiseThrottle({
    requestsPerSecond: 5, // up to 5 requests per second.. This will adjust the request rate
    promiseImplementation: Promise, // the Promise library you are using
});

const scans = [];
// swapped while loop with for loop but it accomplishes effectively the same task
for (let i = 0; i < 1000; i += 30) {
    // closure in order to get correct value of i on each iteration.
    ((min) => {
        scans.push(promiseThrottle.add(() => scan(fruitId, min, min + 30)));
    })(i);
}

Promise.all(scans).then(res => {
    // res is an array of all responses from scans
    // scans completed
});

promise-throttle 在抽象节流方面做得非常好,但 API 需要一秒钟才能习惯..

【讨论】:

    【解决方案2】:

    这是一种使用高阶函数来包装现有函数的方法:

    function throttle(max, fn) {
      let queue = [];
      let inProgress = new Set();
    
      const runTaskNow = async (args) => {
        inProgress.add(args);
        await fn(...args);
        inProgress.delete(args);
    
        const next = queue.shift();
        if (next) {
          runTaskNow(next.args).then(next.resolve, next.reject);
        }
      };
    
      const runTaskLater = args => {
        let resolve, reject;
        const promise = new Promise((yes, no) => {
          resolve = yes;
          reject = no;
        });
        queue.push({ resolve, reject, args });
        return promise;
      };
    
      return (...args) => {
        return inProgress.size < max ? runTaskNow(args) : runTaskLater(args);
      }
    }
    

    要使用,请使用throttle 包装您的函数:

    const scan = throttle(5, (vFruitId, min, max) => {
      return $.ajax({
        // ...
      });
    });
    

    ...然后像您通常调用 scan 一样调用它。

    【讨论】:

    • 那么我将如何实际使用它?像while循环部分?你能告诉我我的while循环部分吗?
    • 我也不明白scan() 函数应该是什么样子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 2016-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多