Пример Async Vala

В книге "Введение в Vala" доктора Майкла Лауэра он упомянул, что асинхронный api lib Soup не работает. Я изо всех сил пытаюсь написать простой пример, используяsession.queue_messageкоторые запрашивают радиостанции с помощью службы из радиобраузера. Вот мой код. Буду признателен за любую помощь опытных программистов, таких как "Эл Томас". Спасибо.

public class Station : Object {

    // A globally unique identifier for the change of the station information
    public string changeuuid { get; set; default = ""; }

    // A globally unique identifier for the station
    public string stationuuid { get; set; default = ""; }

    // The name of the station
    public string name { get; set; default = ""; }

    // The stream URL provided by the user
    public string url { get; set; default = ""; }
    // and so on ... many properties
    public string to_string () {
        var builder = new StringBuilder ();
        builder.append_printf ("\nchangeuuid = %s\n", changeuuid);
        builder.append_printf ("stationuuid  = %s\n", stationuuid);
        builder.append_printf ("name = %s\n", name);
        builder.append_printf ("url = %s\n", url);
        return (owned) builder.str;
    }
}

public class RadioBrowser : Object {
    private static Soup.Session session;
    // private static MainLoop main_loop;
    public const string API_URL = "https://de1.api.radio-browser.info/json/stations";
    public const string USER_AGENT = "github.aeldemery.radiolibrary";

    public RadioBrowser (string user_agent = USER_AGENT, uint timeout = 50)
    requires (timeout > 0)
    {
        Intl.setlocale ();
        session = new Soup.Session ();
        session.timeout = timeout;
        session.user_agent = user_agent;
        session.use_thread_context = true;

        // main_loop = new MainLoop ();
    }

    private void check_response_status (Soup.Message msg) {
        if (msg.status_code != 200) {
            var str = "Error: Status message error %s.".printf (msg.reason_phrase);
            error (str);
        }
    }

    public Gee.ArrayList<Station> listStations () {
        var stations = new Gee.ArrayList<Station> ();
        var data_list = Datalist<string> ();
        data_list.set_data ("limit", "100");

        var parser = new Json.Parser ();
        parser.array_element.connect ((pars, array, index) => {
            var station = Json.gobject_deserialize (typeof (Station), array.get_element (index)) as Station;
            assert_nonnull (station);
            stations.add (station);
        });

        var msg = Soup.Form.request_new_from_datalist (
            "POST",
            API_URL,
            data_list
        );
        // send_message works but not queue_message
        // session.send_message (msg);

        session.queue_message (msg, (sess, mess) => {
            check_response_status (msg);
            try {
                parser.load_from_data ((string) msg.response_body.flatten ().data);
            } catch (Error e) {
                error ("Failed to parse data, error:" + e.message);
            }
        });

        return stations;
       }
    }

    int main (string[] args) {
        var radio_browser = new RadioBrowser ();
        var stations = radio_browser.listStations ();
        assert_nonnull (stations);

       foreach (var station in stations) {
           print (station.to_string ());
       }
       return 0;
    }

1 ответ

Хотя я не Эл Томас, я все же могу помочь.;)

Для работы асинхронных вызовов должен быть запущен основной цикл, обычно из основного потока программы. Таким образом, вы хотите создать и выполнить основной цикл из вашегоmain() функция, а не в коде вашего приложения:

int main (string[] args) {
    var loop = new GLib.MainLoop ();
    var radio_browser = new RadioBrowser ();

    // set up async calls here

    // then set the main loop running as the last thing
    loop.run();
}

Кроме того, если вы хотите дождаться завершения асинхронного вызова, вам обычно необходимо выполнить вызов, используя yield ключевое слово из другой асинхронной функции, например:

    public async Gee.ArrayList<Station> listStations () {
        …

        // When this call is made, execution of listStations() will be
        // suspended until the soup response is received
        yield session.send_async(msg);

        // Execution then resumes normally
        check_response_status (msg);
        parser.load_from_data ((string) msg.response_body.flatten ().data);

        …

        return stations;
    }

Затем вы можете вызвать это из основной функции (не асинхронной), используя listStations.begin(…) обозначение:

int main (string[] args) {
    var loop = new GLib.MainLoop ();
    var radio_browser = new RadioBrowser ();
    radio_browser.listStations.begin((obj, res) => {
        var stations = radio_browser.listStations.end(res);
        …
        loop.quit();
    });
    loop.run();
}

В качестве дальнейшего чтения я бы порекомендовал асинхронный раздел учебника Vala, а также примеры asyc в вики.

Другие вопросы по тегам