【问题标题】:Save JavaScript Promise for future use保存 JavaScript Promise 以备将来使用
【发布时间】:2017-07-11 14:19:33
【问题描述】:

我正在使用 Node.js 构建服务器端 RESTApi。当我自己测试时,Node 工作正常。但是当它真的在那里时,它仍然可能面临溢出问题。当请求很多的时候,比如说有5个以上的child_process(spawn)同时工作,每个进程都需要更长的时间,基本上都会减慢速度。

我的想法是检查当前进程是否低于某个限制(例如,一次限制为 3 个进程),如果超过限制,我将请求保存到一个数组中,并且每当当前进程低于限制,我使用 .shift() 弹出数组中最旧的一个并处理它。

但是,当涉及到 Promise 时,它​​变得很困难,因为我不知道我们是否可以将 Promise 存储到一个数组中,或者我是否应该简单地让进程暂停几秒钟,我认为它不是好主意。

如果你想持有一个承诺并在未来将承诺返回给客户,通常的方式是什么?

对不起,如果我没有说清楚。下面总结一下我的疑惑: 1. 我们可以保存一个承诺以备将来使用吗? 2. 我们是否将它们保存在数组中? 3. 我应该使用其他方法来保持承诺,例如使用 sleep() 还是简单地使用 while 循环来等待这个过程继续进行?

谢谢!

【问题讨论】:

  • Promise 本质上已经是一个代表未来价值的结构。在它创建后的任何时候,当您在其上调用then(cb) 时,它都会为您提供价值。
  • 如果我使用 loadash 会有帮助吗?
  • 您是否考虑过在您的流程中使用promise-queue 包?
  • 是的,承诺一个可以像其他任何东西一样传递的普通值。
  • 听起来您需要更强大的服务器,或者找到并修复性能瓶颈。仅仅排队请求通常不会有帮助——这些队列最终也会溢出。管理它们需要额外的处理能力和内存,因此您通常最好尽快为它们提供服务。

标签: javascript node.js promise


【解决方案1】:

说有5个以上的child_process(spawn)同时工作,每个进程耗时较长,基本上都慢下来了。

在实际部署中 - 您不会以这种方式处理 CPU 密集型任务和子任务 - 您将使用合理的并发数据结构(如 mqtt 或数据库上的队列)并将工作分配给您部署的工作人员然后将其发送回服务器。

原因是服务器总是会宕机——你想防止部分工作。

我的想法是检查当前进程是否低于某个限制(例如,一次限制为 3 个进程),如果超过限制,我将请求保存到一个数组中,并且每当当前进程低于限制,我使用 .shift() 弹出数组中最旧的一个并处理它。

这是执行此操作的代码,请您阅读第一点并且不要在生产中使用该代码,而是在您的部署中限制它(例如,如果您使用 AWS 将规模限制为 3 个实例):

// lifted from my bluebird-api project
function throttle(fn, concurrency = 20, Promise) {
    // create an array of workers as resolved promises
    const workers = Array(concurrency).fill(Promise.resolve());
    const work = []; // pending work
    return function (...args) {
        // when there is work, pull the next worker
        const worker = workers.pop();
        if (worker === undefined) { // if there is no pending worker
            let resolve, reject;
            // store the promise for the result
            const promise = new Promise((res, rej) => {
                resolve = res; 
                reject = rej;
            });
            // and add it to the queue
            work.unshift({ args, resolve, reject, promise });
            return promise;
        }
        // after the worker is ready, call the function
        worker = worker.then(() => (fn(...args), null));
        worker.then(function pump() {
            if (work.length) { // after you're ready
                const {resolve, reject, args} = work.pop();
                // continue draining the queue
                worker = worker.then(() => fn(...args)).then(resolve, reject).then(pump);
            } else { // or declare ready
                workers.push(worker);
            }
            return null;
        });
        return worker;
    }
} 

代码是从bluebird-api 提取的,仍然是 WIP。

如果你想持有一个promise并在未来将这个promise返回给客户,正常的方式是什么?

是的,它完全是一个受支持的案例 - 它不会泄漏内存并且是安全的(在现代 Promise 实现中)。虽然再次 - 这是一个 XY 问题 - 您不应该在节点服务器上以这种方式分发工作。

当您实施正确的解决方案(排队和卸载到不同的服务)时,您可以创建一个 Promise 队列,您可以在其中返回一个 Promise,并在队列准备好后解决它。

【讨论】:

  • 那么在现实世界中,“子进程”通常在不同的服务器实例中?
  • 我实际上是在使用 child_process 在后面运行 python。有更好的方法吗?
  • 在“现实世界”中,您通常会在容器中执行所有这些操作,并且运行 python 将在从队列馈送的不同容器中运行 - 这样您就可以“弹性地扩展 python 进程实例的数量” "。
【解决方案2】:
  1. 我们可以保存一个承诺以备将来使用吗? 2. 我们是否将它们保存在数组中?

您可以将 Promise 存储在一个数组中,这是 Promise 的一部分,它们在需要时才需要评估。

  1. 我是否应该使用其他方法来保持承诺,例如使用 sleep() 或只是一个 while 循环来等待此过程继续进行?

使用睡眠没有错。不要循环那是低效的。

编辑

@Bergi 在 cmets 中说,即使在 Promise 中也不应该使用 sleep

【讨论】:

  • sleep 听起来像是同步阻塞的方法,和循环一样错误
  • @Bergi 我认为 OP 意味着在承诺中睡觉
  • “在承诺中”并不意味着“在后台”。你根本不应该睡觉。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-05
  • 1970-01-01
  • 2023-04-01
  • 2016-01-06
  • 2017-10-15
  • 1970-01-01
相关资源
最近更新 更多