【问题标题】:resolving Promises sequentially按顺序解决 Promise
【发布时间】:2017-10-31 23:09:00
【问题描述】:

编辑:这是一个电子项目,在用户系统上启动了本地服务器。因此,如果多个用户尝试同时访问会发生什么,任何担忧都可以忽略!


我的客户端代码正在生成一个 JavaScript 对象数组,我称之为 packets。可能有无限的 packets,尽管 1 到 10 之间是最常见的用例。

我需要对后端路由进行 API 调用,每个 packet 一个,将 packet 作为参数传递给每个调用。

但是,后端对每个数据包进行一些繁重的计算工作,以至于尝试一次处理超过 2-3 个数据包会使服务器崩溃。

是否可以同步解析 Promise,这样第二个 Promise 仅在第一个解析时触发,第三个在第二个解析后触发,以此类推? p>

我的理解是 Promise.all() 同时进行所有调用,这对我没有帮助。

(我也知道这是一种反模式,但是对于我的特定要求,我不确定还能做什么)

我知道这纯粹是抽象的,但任何想法都将不胜感激!!!

【问题讨论】:

  • 是的,“顺序”会是更好的语义。我只是说 Promise 1 需要在 Promise 2 开始之前完成,Promise 2 需要在 Promise 3 开始之前完成,依此类推,直到 Promise N。
  • My client code is generating an array of JavaScript objects - 让我问这个。您是否正在创建数组,一旦生成了数组,您就可以进行 API 调用吗?或者更多的情况是这些对象是动态创建的,并且您希望确保一次只调用一个 API - 即下面的“队列”答案将是理想的
  • 听起来你只是在描述chaining。使用composition
  • 一种选择是将其视为Promise-throttling problem,其中n = 1,尽管听起来您也可以使用n = 2n = 3

标签: javascript asynchronous promise


【解决方案1】:

用 Promises 变得奇怪

一个异步队列,我的previous answers 的一个衍生;我添加了随机完成时间来模拟真实环境:

class Queue {
    constructor() {
        this.queue = [];
    }

    enqueue(obj) {
        return this.queue.push(obj);
    }

    dequeue() {
        return this.queue.shift();
    }

    hasWork() {
        return (this.queue.length > 0);
    }
}

class AsyncQueue extends Queue {
    constructor(job) {
        super();
        this.job = job;
    }

    process(cb) {
        return this.job(this.dequeue()).then(data => {
            cb(data);
            if (this.hasWork())
                return this.process(cb);
        });
    }
}

//MUST RETURN Promise
function work() {
    var duration = chooseDelay();
    console.log('START JOB, I.E., MAKE REQUEST (will take %s)', duration);
    return t_o(duration);
}

function report() {
    console.log('JOB DONE');
}

function done() {
    console.log('ALL WORK DONE');
}

function t_o(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve();
        }, delay);
    });
}

function chooseDelay() {
    var delays = [200, 1000, 4000, 100, 50, 7000];
    return delays[parseInt(Math.random() * 100) % delays.length];
}

var q = new AsyncQueue(work);

for (var packet = 0; packet < 10; ++packet)
    q.enqueue(packet);

q.process(report).then(done);

【讨论】:

  • 有趣!我会试一试并报告!
  • 这真是太好了。类和辅助函数的大量使用,非常清晰。您可以通过使用Promise.resolve() 包装this.job() 调用来解决不返回承诺的问题,但也许这不是您想要静默允许的事情,因为它可能表示例如忘记返回承诺之类的错误。跨度>
  • @GregL 谢谢!!
  • 是否可以访问每个调用的结果?
  • 当然,report 有一个 data 参数
【解决方案2】:

作为一个很好的答案的替代方案,这是一个非常简单的队列(work 函数无耻地从其他答案复制和扩展)

    // Here is the queue "creator"
    let promiseQueue = fn => {
        let q = Promise.resolve();
        return (...args) => q = q.then(() => fn(...args));
    };
    // that's it, that's the whole code for a promise queue

    // here we create a queue
    var q = promiseQueue(work);

    // simple mock asynchronous function
    function work({index, data}) {
        var duration = parseInt(Math.random() * 100) + 100;
        console.log('START JOB %s, I.E., MAKE REQUEST (will take %s) and should result with %s', index, duration, (index +1) * data);
        return new Promise(resolve => setTimeout(resolve, duration)) // mock a delay
        .then(() => ({index, result:(index + 1) * data})); // and some "processing"
    }


    // simulating two "chunks" of packets, generated a millisecond apart, but still, the sequence will be maintained
    setTimeout(() => {
        var packets = Array.from({length:10}, (_, index) => ({index, data:parseInt(Math.random() * 10000)}));
        var promises = packets.map(packet => q(packet));
        // the results in promise all are all the results of this batch of "packets"
        Promise.all(promises).then(results => console.log(results));
    }, 100);
    setTimeout(() => {
        var packets = Array.from({length:10}, (_, index) => ({index: index + 10, data:parseInt(Math.random() * 10000)}));
        var promises = packets.map(packet => q(packet));
        Promise.all(promises).then(results => console.log(results));
    }, 101);

【讨论】:

    【解决方案3】:

    顺序执行promise的简单函数

    const sequentiallyExecuting = (promises) => {
        let promise = Promise.resolve();
        promises.forEach((task) => {
            promise = promise.then((data) => {
                return task;
            })
        });
    
        return promise;
    }
    // pass array of promises to this function
    sequentiallyExecuting(promises).then((data) =>{
       console.log("all requests completed sequentially");
    })
    

    【讨论】:

      【解决方案4】:

      'use strict';
      
      // job to be done
      function job(params) {
      	return function () {
      		console.log('job started', params);
      		return new Promise(function (resolve) {
      			setTimeout(function () {
      				console.log('job finished');
      				resolve();
      			}, 1000);
      		})
      	}
      }
      
      // data to be processed sequentially 
      var params = [
      	1,
      	2,
      	3,
      	4,
      	5
      ];
      // reduce data to Promise sequence
      params.reduce(function (cum, cur) {
      	return cum.then(job(cur));
      }, Promise.resolve());

      【讨论】:

        【解决方案5】:

        使用 async/await 它变得微不足道:

        while (promiseArray.length > 0)
                await promiseArray.shift();
        

        【讨论】:

          猜你喜欢
          • 2017-06-21
          • 1970-01-01
          • 2019-10-09
          • 1970-01-01
          • 2021-04-27
          • 1970-01-01
          • 1970-01-01
          • 2021-01-05
          • 2021-01-18
          相关资源
          最近更新 更多