Search code examples
javascriptasynchronousgjsmainloop

Asynchronous Soup calls


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.


Solution

  • 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.