admin管理员组

文章数量:1023773

I'm trying to make a Tampermonkey script to update dates on some site. I got an array of id's from a site, and I'm requesting data from it with the id of the array. After that, I have to return data of each Input.

As the function is async, it returns data in a random order, but I need those new arrays to return in the original order. I have tried sync and Promises, but the first is too slow and I haven't understood the second.

I can sort ids, but I also got the dates which are in the order of the first Array, so I don't know how to achieve the same order as the second id array.

Here's the code:

id = GM_getValue('id');

for (let i = 0; i < id.length; i++) {
  setTimeout(() => {
      console.log("Updating " + (i + 1) + " Title");

      GM_xmlhttpRequest({
          method: "GET",
          url: "***" + id[i] + "/***",
          onload: function(response) {
            $(response.responseText).find("#main-form :input").each(function(x) {
                if (x == 0) ids.push(parseInt($(this).val()));
                if (x == 1) array.push($(this).val()));
            });
        }
      });
  }, i * 333);
}

I'm trying to make a Tampermonkey script to update dates on some site. I got an array of id's from a site, and I'm requesting data from it with the id of the array. After that, I have to return data of each Input.

As the function is async, it returns data in a random order, but I need those new arrays to return in the original order. I have tried sync and Promises, but the first is too slow and I haven't understood the second.

I can sort ids, but I also got the dates which are in the order of the first Array, so I don't know how to achieve the same order as the second id array.

Here's the code:

id = GM_getValue('id');

for (let i = 0; i < id.length; i++) {
  setTimeout(() => {
      console.log("Updating " + (i + 1) + " Title");

      GM_xmlhttpRequest({
          method: "GET",
          url: "***" + id[i] + "/***",
          onload: function(response) {
            $(response.responseText).find("#main-form :input").each(function(x) {
                if (x == 0) ids.push(parseInt($(this).val()));
                if (x == 1) array.push($(this).val()));
            });
        }
      });
  }, i * 333);
}
Share Improve this question edited Jan 4, 2021 at 7:22 double-beep 5,53719 gold badges40 silver badges49 bronze badges asked Jan 2, 2021 at 20:43 NurarihyonNurarihyon 571 silver badge6 bronze badges 6
  • You can't guarantee the order those asynchronous requests get returned, even with the 333ms delay between each being sent. Using an array of promises and Promise.all() would fix that – charlietfl Commented Jan 2, 2021 at 21:38
  • @charlietfl Oh... Completly forgot that this is asynchronous... Stupid mistake... Thanks for help! – Nurarihyon Commented Jan 2, 2021 at 21:59
  • @charlietfl So... I read about Promises, but I have no idea how it works :/ Could You give me an example? – Nurarihyon Commented Jan 3, 2021 at 15:37
  • Even though the order of the response are not predictable, the callback that gets called is tied to the request. That is, The callback for id[0] will be called for the id[0] request even though this may happen after id[1]. The simple solution to get the responses with the original order is to set the arrays instead of using .push(): ids[i] = ... and array[i] = ... – slebetman Commented Jan 4, 2021 at 7:48
  • @slebetman I actually tried it, but strange things was occurring – Nurarihyon Commented Jan 4, 2021 at 15:34
 |  Show 1 more ment

2 Answers 2

Reset to default 5

You can use Promises to execute the GET requests in a specific order. Here's an example:

function makeGetRequest(url) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: response => resolve(response.responseText),
            onerror: error => reject(error)
        });
    });
}

const ids = GM_getValue('id');
for (const id of ids) {
    try {
        const response = await makeGetRequest("***" + id + "/***");
        // do stuff with response
    } catch (error) {
        // in case the GET request fails
        console.error(
            "Request failed with error code", error.status,
            ". Message is ", error.responseText
        );
    }
}

In this example, I've created a makeGetRequest() function that returns a promise, which is resolved when the GET request succeeds, and rejected when it fails.

await waits for the Promise to settle before moving on and the try exists to catch Promise rejection (in case the request fails).

References:

  • Promise on MDN.
  • await on MDN.

TypeScript:

export enum HttpDataType {
  JSON = "JSON"
}

import {HttpDataType} from "./enum/HttpDataType";

export default class Http {

  static get(option: { url: string, dataType?: HttpDataType, synchronous?: boolean, onload?: Function }) {
    option['method'] = 'GET';

    if (option.synchronous) {
      return new Promise((resolve, reject) => {
        // @ts-ignore
        GM_xmlhttpRequest({
          ...option,
          onload: (response) => {
            resolve(option.dataType === HttpDataType.JSON ? JSON.parse(response.responseText) : response.responseText);
          },
          onerror: (error) => {
            reject(error);
          }
        });
      })
    } else {
      const onload1 = function (details) {
        let response;
        if (option.dataType === HttpDataType.JSON) {
          response = JSON.parse(details.responseText);
        } else {
          response = details.response;
        }
        option.onload(response);
      }
      // @ts-ignore
      GM_xmlhttpRequest({...option, onload: onload1});
    }
  }
}

static async getXxx() {
  let response = await Http.get({
    url: '……',
    dataType: HttpDataType.JSON,
    synchronous: true
  });

  // @ts-ignore
  if (!response || response.status !== 'success') {
    console.error('getXxx error', response);
  }

  // @ts-ignore
  return response.data;
}

this.getXxx().then((data: any) => {
  // ……
});

I'm trying to make a Tampermonkey script to update dates on some site. I got an array of id's from a site, and I'm requesting data from it with the id of the array. After that, I have to return data of each Input.

As the function is async, it returns data in a random order, but I need those new arrays to return in the original order. I have tried sync and Promises, but the first is too slow and I haven't understood the second.

I can sort ids, but I also got the dates which are in the order of the first Array, so I don't know how to achieve the same order as the second id array.

Here's the code:

id = GM_getValue('id');

for (let i = 0; i < id.length; i++) {
  setTimeout(() => {
      console.log("Updating " + (i + 1) + " Title");

      GM_xmlhttpRequest({
          method: "GET",
          url: "***" + id[i] + "/***",
          onload: function(response) {
            $(response.responseText).find("#main-form :input").each(function(x) {
                if (x == 0) ids.push(parseInt($(this).val()));
                if (x == 1) array.push($(this).val()));
            });
        }
      });
  }, i * 333);
}

I'm trying to make a Tampermonkey script to update dates on some site. I got an array of id's from a site, and I'm requesting data from it with the id of the array. After that, I have to return data of each Input.

As the function is async, it returns data in a random order, but I need those new arrays to return in the original order. I have tried sync and Promises, but the first is too slow and I haven't understood the second.

I can sort ids, but I also got the dates which are in the order of the first Array, so I don't know how to achieve the same order as the second id array.

Here's the code:

id = GM_getValue('id');

for (let i = 0; i < id.length; i++) {
  setTimeout(() => {
      console.log("Updating " + (i + 1) + " Title");

      GM_xmlhttpRequest({
          method: "GET",
          url: "***" + id[i] + "/***",
          onload: function(response) {
            $(response.responseText).find("#main-form :input").each(function(x) {
                if (x == 0) ids.push(parseInt($(this).val()));
                if (x == 1) array.push($(this).val()));
            });
        }
      });
  }, i * 333);
}
Share Improve this question edited Jan 4, 2021 at 7:22 double-beep 5,53719 gold badges40 silver badges49 bronze badges asked Jan 2, 2021 at 20:43 NurarihyonNurarihyon 571 silver badge6 bronze badges 6
  • You can't guarantee the order those asynchronous requests get returned, even with the 333ms delay between each being sent. Using an array of promises and Promise.all() would fix that – charlietfl Commented Jan 2, 2021 at 21:38
  • @charlietfl Oh... Completly forgot that this is asynchronous... Stupid mistake... Thanks for help! – Nurarihyon Commented Jan 2, 2021 at 21:59
  • @charlietfl So... I read about Promises, but I have no idea how it works :/ Could You give me an example? – Nurarihyon Commented Jan 3, 2021 at 15:37
  • Even though the order of the response are not predictable, the callback that gets called is tied to the request. That is, The callback for id[0] will be called for the id[0] request even though this may happen after id[1]. The simple solution to get the responses with the original order is to set the arrays instead of using .push(): ids[i] = ... and array[i] = ... – slebetman Commented Jan 4, 2021 at 7:48
  • @slebetman I actually tried it, but strange things was occurring – Nurarihyon Commented Jan 4, 2021 at 15:34
 |  Show 1 more ment

2 Answers 2

Reset to default 5

You can use Promises to execute the GET requests in a specific order. Here's an example:

function makeGetRequest(url) {
    return new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: "GET",
            url: url,
            onload: response => resolve(response.responseText),
            onerror: error => reject(error)
        });
    });
}

const ids = GM_getValue('id');
for (const id of ids) {
    try {
        const response = await makeGetRequest("***" + id + "/***");
        // do stuff with response
    } catch (error) {
        // in case the GET request fails
        console.error(
            "Request failed with error code", error.status,
            ". Message is ", error.responseText
        );
    }
}

In this example, I've created a makeGetRequest() function that returns a promise, which is resolved when the GET request succeeds, and rejected when it fails.

await waits for the Promise to settle before moving on and the try exists to catch Promise rejection (in case the request fails).

References:

  • Promise on MDN.
  • await on MDN.

TypeScript:

export enum HttpDataType {
  JSON = "JSON"
}

import {HttpDataType} from "./enum/HttpDataType";

export default class Http {

  static get(option: { url: string, dataType?: HttpDataType, synchronous?: boolean, onload?: Function }) {
    option['method'] = 'GET';

    if (option.synchronous) {
      return new Promise((resolve, reject) => {
        // @ts-ignore
        GM_xmlhttpRequest({
          ...option,
          onload: (response) => {
            resolve(option.dataType === HttpDataType.JSON ? JSON.parse(response.responseText) : response.responseText);
          },
          onerror: (error) => {
            reject(error);
          }
        });
      })
    } else {
      const onload1 = function (details) {
        let response;
        if (option.dataType === HttpDataType.JSON) {
          response = JSON.parse(details.responseText);
        } else {
          response = details.response;
        }
        option.onload(response);
      }
      // @ts-ignore
      GM_xmlhttpRequest({...option, onload: onload1});
    }
  }
}

static async getXxx() {
  let response = await Http.get({
    url: '……',
    dataType: HttpDataType.JSON,
    synchronous: true
  });

  // @ts-ignore
  if (!response || response.status !== 'success') {
    console.error('getXxx error', response);
  }

  // @ts-ignore
  return response.data;
}

this.getXxx().then((data: any) => {
  // ……
});

本文标签: javascriptHow can I use async GMxmlhttpRequest to return values in original orderStack Overflow