2

I'm working on simple extension for Gnome DE and have some trouble wrapping my head around asynchronous Soup calls and the event loop.

Here's what I have:

_httpSession = new Soup.Session();

let token = 'sometoken'
let url = 'someurl';
let _allData = [];
let elements = [1,2];

for (let el of elements) {
    let message = Soup.form_request_new_from_hash('GET', url + el, { access_token: token });
    _httpSession.queue_message(message, () => {
        if (message.status_code != Soup.KnownStatusCode.OK) {
            _error(message.status_code.toString());
        }
        try {
            message = JSON.parse(message.response_body.data).items;
        } catch (e) {
            _error(e.toString());
        }
        _allData = _allData.concat([el, message]);
    });
}

Given the asynchronous calls in a for loop above, how to make sure that _allData.concat() has been executed for all iterations? I want to print out the _allData variable, but only when concatenations for each el were executed.

1 Answers1

2

Easiest way is probably an async-await pattern:

// Your request function can be turned into a Promise:
function requestFunc(session, message) {
    return new Promise((resolve, reject) => {
        session.queue_message(message, () => {
            try {
                if (message.status_code === Soup.KnownStatusCode.OK) {
                    let result = JSON.parse(message.response_body.data);

                    resolve(result);
                } else {
                    reject(new Error(message.status_code.toString()));
                }
            } catch (e) {
                reject(e);
            }
        });
    });
}

// Then you can await each in a loop
async function logRequestResults(session, url, token, elements) {
    try {
        let results = [];

        for (let el of elements) {
            let message = Soup.form_request_new_from_hash('GET', url + el, {
                access_token: token
            });

            let result = await requestFunc(session, message);

            results = results.concat([el, results.items]);
        }

        // Success; all requests completed
        log(results);
    } catch (e) {
        // An error occurred somewhere in the loop
        logError(e);
    }
}

// Using the function
_httpSession = new Soup.Session();

let token = 'sometoken'
let url = 'someurl';
let elements = [1,2];

logRequestResults(session, url, token, elements);

Depending what you're actually doing with your results, you might want to refactor that. The important part is turning a func(something, () => {}) pattern into a Promise you can await in a loop.

Also note, that async functions implicitly return a Promise, those those can be used with await as well.

andy.holmes
  • 3,383
  • 17
  • 28
  • 1
    Much appreciated Andy. Before your reply, I actually stumbled upon your blog post: https://andyholmes.github.io/articles/asynchronous-programming-in-gjs.html Thanks a lot for helping out the community :) – Juozas Miškinis Feb 13 '20 at 22:56