简体   繁体   English

Node.js 返回一个没有立即调用的 function

[英]Node.js Return a function that is not invoked instantly

I'm working on a library to interact with the discord API - and hence I need a ratelimiting system.我正在开发一个库来与 discord API 交互 - 因此我需要一个速率限制系统。

I of course need to be able to make REST requests, which I've seperated into a standalone part of my app, for example I might run:我当然需要能够发出 REST 请求,我已将其分离为我的应用程序的独立部分,例如我可以运行:

await this.client.rest.get('hello/hello') (Naming unrelated) await this.client.rest.get('hello/hello') (命名无关)

I then make a request to my redis cache to fetch the current rate limit, and should we currently be passed it, I place the request in an array (my queue), until the ratelimit resets.然后我向我的 redis 缓存发出请求以获取当前的速率限制,如果我们当前通过它,我将请求放在一个数组(我的队列)中,直到速率限制重置。

The issue I am having is working out how to return a value to the above function, even if it's been placed in a queue etc?我遇到的问题是如何将值返回到上述 function,即使它已被放入队列等? I've attached my code below.我在下面附上了我的代码。

import axios, { AxiosRequestHeaders, AxiosResponse } from "axios";
import { InternalRequest, REST } from ".";
import { BucketHandler } from "./BucketHandler.js";

export class RequestManager {
  REST: REST;
  ratelimit: any;
  handlers: BucketHandler[];
  constructor(REST: REST) {
    this.REST = REST;

    this.ratelimit = {
      queue: [],
      timer: null,
      time: null,
    };

    this.handlers = [];
  }

  private async manageQueue() {
    if (this.ratelimit.queue.length < 1) return;
    if (this.ratelimit.timer) return;
    this.ratelimit.time = await this.REST.redisClient.ttl("ratelimit:global");
    this.ratelimit.timer = setTimeout(() => {
      this.ratelimit.timer = null;
      this.processQueue();
    }, this.ratelimit.time * 1500);
  }

  private async processQueue() {
    const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global"));
    this.request(this.ratelimit.queue.shift());
    if (globalRateLimit < 2 && this.ratelimit.queue.length > 0) this.processQueue();
    else this.manageQueue();
  }

  private async parseResponse(res: AxiosResponse) {
    return res.data;
  }

  private formatRequest(options: InternalRequest) {
    const url: string = `${this.REST.api}/v${this.REST.version}${options.fullRoute}`;
    const headers: AxiosRequestHeaders = {};
    if (options.requestOptions.authorization) headers.Authorization = `${options.requestOptions.authorizationPrefix} ${this.REST.token}`;

    return { url, headers };
  }

  public async raw(options: InternalRequest) {
    const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global"));
    if (globalRateLimit > 2) {
      await this.REST.redisClient.decr("ratelimit:global");
      const queue = this.ratelimit.queue.push(options);
      this.manageQueue();
      return queue
    } else {
      return this.request(options);
    }
  }

  private async request(options: InternalRequest) {
    console.log("REQUEST RUN!");
    const request = this.formatRequest(options);

    const res = await axios(request.url, {
      method: options.requestMethod,
      headers: request.headers,
    }).catch((err) => {
      console.log("ERROR!");
    });

    return res;
  }
}

I think you are looking to return a promise that resolves when the request has been made.我认为您希望返回一个 promise ,它会在发出请求时解决。

You should also make sure to handle rate limit errors received from the API.您还应该确保处理从 API 收到的速率限制错误。

The logic should look like this:逻辑应该如下所示:

manager.request(whatever) {
    // basically a deferred with resolve and reject
    let resolver = { res: null, rej: null, request: whatever };
    const promise = new Promise((res, rej) => {   
         resolve.res = res; resolver.rej = rej;
    });
    this.queue.push(resolver);
    this.startQueueProcessor();
    return promise;
}
startQueueProcessor() {
    // start timeout if not started already, call the real processor ->
    // limit the number of items you are processing
    // process items from queue
    const item = queue.pop();
    // make sure to re-throw error, otherwise the .then part will be run
    this.makeRequest(item).catch(e => item.rej(e)).then(res => item.res(e));
    // start new timeout. 
    // I hate intervals but you can do the same with intervals
}

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

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