【问题标题】:Execute at most N tasks in parallel using JS promises or generators使用 JS 承诺或生成器并行执行最多 N 个任务
【发布时间】:2017-08-27 11:05:18
【问题描述】:

我想实现一个 JS 类,它使用 JS 承诺或生成器并行执行最多 N 个任务。类似的东西

class Executor {
    constructor(numberOfMaxTasks) {
    ...
    }

    next(task) {
    ...
    }

    done(onDone) {
    ...
    }
}

....
const executor = new Executor(2);
executor.next(task1).next(task2).next(task3).done(onDone);

task1 和 task 2 应该并行执行,而 task3 应该等到前面的任务之一完成。当所有任务完成时执行 onDone 回调。

我试图使用 Promise 来实现它,但我失败了。我是生成器的新手,目前不知道他们是否可以在这里提供帮助。这主要是为了学习目的,这就是为什么我不想使用任何第三方库,只是原生 JS。任何提示都会很棒,提前谢谢!

【问题讨论】:

  • 您知道 JavaScript 不会并行运行代码,除非您生成 WebWorkers 或等效的 node.js,对吧? Promise 和其他异步内容允许您在后台运行非 JavaScript 内容(网络 IO 等)时运行代码。
  • 请告诉我们你的尝试。
  • 不要再对异步代码使用生成器了。向右转async/await 语法。
  • @Touffy 是的,你是对的。也许并行在这里不是最好的词。我对这个标题有点困惑:glebbahmutov.com/blog/run-n-promises-in-parallel。可能异步而不是并行会更好。

标签: javascript ecmascript-6 generator es6-promise


【解决方案1】:

我认为你最好使用bluebird promise library

例如,bluebird provides map function 可以为所欲为:

  • 在第一个参数中,您可以指定任务执行所需的数据数组
  • 在第二个参数中,您可以指定映射器函数,该函数可以实际运行您的任务并返回承诺
  • 在第三个参数中,您可以使用{ concurrency: N } 对象指定“当时最多 N 个任务”。

请注意,对于bluebird.map 函数,不能保证执行顺序。唯一保证bluebird.map 的结果将通过相同顺序的结果数组来实现。

使用该函数,您可以在没有 Executor 类的情况下重写您的代码(例如 node.js):

const Promise = require('bluebird')
const os = require('os')
const task1Data = 1000
const task2Data = 5000
const task3Data = 3000

const tasksData = [ task1Data, task2Data, task3Data ]
function taskExecutorMapper(taskData) {
  // here is place for your code that actually runs
  // asynchronous operation based on taskData and returns promise
  // I'll use setTimeout here for emulate such operation
  return new Promise(function(resolve) {
    setTimeout(resolve, taskData)
  }
}
const tasksPromise = Promise.map(
  tasksData,
  taskExecutionMapper,
  { concurrency: os.cpus().length })
  .then(onDone)

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    最近我不得不解决一个面试问题,类似于您想要实现的目标,但基于 Node.js
    关键是使用类属性(在我的示例中,this.running)控制同时执行的任务数量。因此,对于一组任务,您可以通过 while 循环运行它们,并检查每个循环是否有任何可用的插槽(由 this.running 和 LIMIT 控制),然后运行 ​​promise。
    此代码可能会对您有所帮助。

        let tasks = [];
        let tasksDone = [];
        const LIMIT = 10;
    
        class Executor {
            constructor () {
                this.running = 0;
                for (let i = 0; i < 1000; i++) {
                    tasks[i] = {
                        id: 'job_' + (i+1),
                        time: ((i % 4) + 1) * 25
                    };
                }
            }
    
            checkConcurrency () {       
                if( this.running > LIMIT ){
                    throw new Error('Maximum number of tasks ['+LIMIT+'] reached');
                }
            }
    
            execute (task) {
                return new Promise((resolve, reject)=>{
                    this.running ++;
                    this.checkConcurrency();
                    setTimeout(()=>{
                        this.checkConcurrency();
                        this.running --;
                        resolve({
                    ...task,
                            finished: Date.now()
                        });
                    }, task.time);
                })
    
            }
    
            run () { 
              this.startTime = Date.now();
              this.executeTasks(tasks.slice(0));
            }
    
            executeTasks(tasks) {   
              while (this.running < LIMIT && tasks.length > 0) {
                let task = tasks.shift();
                this.execute(task).then( result => {
                  tasksDone.push(result);
                  if (tasks.length > 0) {
                    this.executeTasks(tasks);
                  }
                });      
              }   
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2016-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多