Click here to Skip to main content
15,877,939 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi, I've been working on a project that I want to make better, I had this upgrade idea on it, And it's requesting multiple times from an API at a short time, Now the problem here is that, How can I order the responses according to which request was sent first? In-depth, My code here requests multiple times in a for loop:

JavaScript
var queue = [];

function Request(text) {

  console.log(text);
  async function Test(){
  await Api.request(); //This is an example and not a real API request function
  let result = Api.response //
  var URL = result.Object();
      queue.push(URL);  
      var I = queue.shift();
      console.log(I)
  }
  Test()
}

for (Something) { // Yet again and example here.
  
    setTimeout(function() {
      Request(text);
}, 2000);
}


This is my code that requests from an API multiple times according to the for loop rule, For this sake, I made it clear as an example and not real API functions etc...

Let's say that the rule for the for loop here makes it that the API returns 3 responses
Response1 // Took 1 second to respond
Response3 // Took 1.5 seconds to respond
Response2 // Took 2 seconds to respond

Notice that the responses are not ordered accordingly, And they're randomized, Since Response3 Could be faster than Response2 and the examples goes on....

Now of course, Each response depending on the API will sometimes get eariler/later than the others, Which makes the problem here and my question here is that:

How can I know that this response came earlier/later but belongs to the first request, And same goes to the 2 other requests.

Request1: Response 1 // Took 1 second to respond
Request2: Response 2 // Took 2 seconds to respond
Request3: Response 3 // Took 1.5 seconds to respond


It doesn't matter if the code have to wait for Response 2 to come before Response 3 even though Response 3 came before Response 2.

I hope this made any sense, I'm sorry for my bad English. Thanks in advance.



(EDIT): Since it's too hard to explain what I'm trying to achieve, I'll post an actual piece of my code here:

var text = "How's work this week? | It's been pretty good not going to lie. It has been a pretty decent day as well, I like how everything is going this week, |  I hope it get's better."
const API  = require('SomeApiWrapperLibrary.js');
var queue = [];
async function processText(text) {

    var Object1 = "Something"
    const Rw = new Api.Client({
        usernameOrEmail: "something",
        password: "somethingelse"
    });
    async function Test(){
      console.log(text);
    await Rw.start(); //required
    let result = await Rw.makeTTS(voice, text);
    var URL = result.Object();
        queue.push( text + " : " + URL);         // queue is now [URL]
        var I = queue.shift();
        console.log(I)
    }
    Test()
}


const prefix = ' | ';

const splitText = text.split(prefix);

async function Request(){

for (const text of splitText) {


 await processText(text).then(console.log('Done'))

  
}

}
Request()


What I have tried:

What I tried here is to number-ize the response that's being pushed to the queue as you can see here:

JavaScript
var queue = []; 

var Number = 0;

function Request(text) {

  console.log(text);
  async function Test(){
  await Api.request(); //This is an example and not a real API request function
  let result = Api.response //
  Number++
  var URL = result.Object();
      queue.push( "URL " + Number + URL);  
      var I = queue.shift();
      console.log(I)
  }
  Test()
}

for (Something) { // Yet again and example here.
  
    setTimeout(function() {
      Request(text);
}, 2000);
}


But that's totally wrong because the number here Will always be from 1 to 3, Even if let's say the 2nd response was meant to be the first one, It'll still be called 2, If that makes any sense.
Posted
Updated 6-Jan-23 0:48am
v6

You don't. API requests can vary in the length of time they take to process and return a result. Every request is handled independently from all others, and none of them have any idea the others even exist.

If you want the results returned in the same order the requests were made, you'd have to make the requests, one at a time, and wait for each to return before making the next request.
 
Share this answer
 
v2
Comments
ftk789 6-Jan-23 0:41am    
Thank you for your reply, I didn't think it's impossible to actually do such a thing, The idea looked pretty simple, But it seems that you have to wait for the first response then request the other, Which is slow for a fast app/program/project, But it's better than nothing, Thank you soo much for your reply man, I hope you have a great rest of your day!
Richard Deeming 6-Jan-23 5:50am    
You don't have to wait for the first request to complete before you start the second; you just have to wait for the first request to complete before you process the second.
ftk789 6-Jan-23 6:04am    
True, Now my question changes honestly, I need somehow to request from that for loop. But then somehow pause the for loop until the response comes in, Then request with the second Request. But yea...
Richard Deeming 6-Jan-23 6:27am    
Difficult to answer precisely without knowing exactly what your code is doing. But I've posted an example which might get you a bit further.
As I mentioned in the comments to solution 1, you don't need to wait for the first request to finish before you start the second request. You just need to wait for the first request to complete before you try to process the second request.

I'd be inclined to store the Promise objects[^] in the queue. Then, when you come to process them, you'll need to await them.

Eg:
JavaScript
class PromiseQueue {
    #queue = [];
    enqueue(promise) {
        this.#queue.push(promise);
    }
    [Symbol.asyncIterator]() {
        return {
            queue: this.#queue,
            async next() {
                if (this.queue.length === 0) {
                    return { done: true };
                }
                
                const promise = this.queue.shift();
                const value = await promise;
                return { done: false, value };
            }
        };
    }
}

// Just for an example - returns a promise which will resolve with the given state after a specified delay:
const waitFor = async (ms, result) => new Promise(resolve => setTimeout(() => resolve(result), ms));

(async () => {
    const taskQueue = new PromiseQueue();
    console.time("TaskQueue");
    
    taskQueue.enqueue(waitFor(2000, "Task 1"));
    taskQueue.enqueue(waitFor(1000, "Task 2"));
    
    for await (let result of taskQueue) {
        console.log(result);
    }
    
    console.timeEnd("TaskQueue");
})();
In this example, the "Task 1" and "Task 2" are output in order, and the total time taken is a touch over 2 seconds. If you'd waited for the first task to finish before starting the second, the total time would have been a touch over 3 seconds.

Edit:
Based on your updated question, I'm guessing you want something like this:
JavaScript
const API  = require('SomeApiWrapperLibrary.js');

class PromiseQueue {
    #queue = [];
    enqueue(promise) {
        this.#queue.push(promise);
    }
    [Symbol.asyncIterator]() {
        return {
            queue: this.#queue,
            async next() {
                if (this.queue.length === 0) {
                    return { done: true };
                }
                
                const promise = this.queue.shift();
                const value = await promise;
                return { done: false, value };
            }
        };
    }
}

(async () => {
    const queue = new PromiseQueue();
    const prefix = ' | ';
    const text = "How's work this week? | It's been pretty good not going to lie. It has been a pretty decent day as well, I like how everything is going this week, |  I hope it get's better."
    
    const Rw = new Api.Client({
        usernameOrEmail: "something",
        password: "somethingelse"
    });
    
    await Rw.start();
    
    const processText = async (textBlock) => {
        const result = await Rw.makeTTS(voice, textBlock);
        return result.Object();
    };
    
    const splitText = text.split(prefix);
    for (const textBlock of splitText) {
        queue.enqueue(processText(textBlock));
    }
    
    for await (let result of queue) {
        console.log(result);
    }
    
    console.log("Done");
})();
 
Share this answer
 
v3
Comments
ftk789 6-Jan-23 6:39am    
That's a really good example, But the thing here is that, the time is specified here, But the time from the API is not, It could be anywhere between 1 second to min's, Plus, This does not go with any loop. Since for loops cannot wait at all, As expected, Unless adding a manual delay to the loop, Which sucks sadly, But this is a good example honestly, I'll make this as a solution, Although it didn't help me a lot, But it's a very good and close example of what I was trying to do, Except it's not an API Request, Thank you man, Really nice of you taking some of your time to respond. (EDIT): To clarify some stuff, Yes it could work in a for loop, But when having a lot of tasks being queued to an API as well, It really does take time, And it won't really be efficient, But yea.
Richard Deeming 6-Jan-23 6:45am    
The specified times are just an example to demonstrate that the queue works as required. You can replace the waitFor calls with any method that returns a Promise, such as a fetch call, and it will still work.
ftk789 6-Jan-23 6:43am    
In fact, I'm going to add a solution, It's not really a solution, But it's my literal code in the work, To show you what i'm trying to really do.
Richard Deeming 6-Jan-23 6:43am    
Rather than adding a non-solution, you should edit your question to add the extra details.
ftk789 6-Jan-23 6:46am    
Done deal, on the bottom of the question.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900