void main()
{
    auto content = getContent("http://httpbin.org/get");
    auto json = parseJSON(content);
    writeln(json);
}

Так круче:


void main()
{
    writeln( "http://httpbin.org/get".getContent.parseJSON );
}

queryParams("name", "bob", "age", 101)

Как-то это криво, лучше бы использовали словари:


queryParams([ "name" :  "bob" , "age" : "101" ])

Я так понимаю кроме POST и GET другие методы не поддерживаются?

Сорри, забыл ответить на вторй вопрос.
Поддерживаются любые типы запросов, не только POST и GET, об этом подробно — во второй части.
Словарь тоже можно использовать, так сработает:
getContent("http://httpbin.org/get", ["a":"hello", "b":"world"]);

Но есть ситуации, когда имя параметра повторяется:
getContent("http://httpbin.org/get", queryParams("name[]", "hello", "name[]", "world"));

И значения в словаре должны иметь один тип. Такое без предварительного преобразования в строку нельзя использовать:
getContent("http://httpbin.org/get", ["name":"bob", "age":42]);

Вместо повторяющихся параметров, можно сделать массивы в качестве значений:


"http://httpbin.org/get".getContent([ "name" : [ "hello", "world" ] , "age" : [ 42.to!string ]  ]);

А что типы надо преобразовывать, так это нормально для языка со строгой типизацией.

В качестве аргумнта для одного из вариантов getContent() принимается словарь типа string[string] куда можно сложить всё что угодно. В другом варианте можно использовать queryParams(), куда тоже можно сложить что угодно, используя другой синтаксис.

Оба метода имеют сви плюсы и минусы, пользователь может выбрать то, что ему в данном случае подходит больше.
Нет однозначно правильного способа конвертить такие массивчики в параметры урла. PHP умеет их назад в массив собирать, но это не обязательно будет делать каждый сервер.

Тут преобразование вполне однозначное:


"http://httpbin.org/get".getContent([ "name[]" : [ "hello", "world" ] , "age" : [ 42.to!string ]  ]);

http://httpbin.org/get?name[]=hello&name[]=world&age=42
Я не имел в виду, что преобразование неоднозначное, а то, что оно не стандартное. Кто мне запретит представлять массивчики так ?name=[hello,world]&age=42 или так ?name=(hello,world)&age=42, да мало ли вариантов. И затачивать код в либе под ПХПшный вариант не вижу смысла.

Представляйте, никто вам не мешает:


"http://httpbin.org/get".getContent([
    "name" : [ "[" ~ [ "hello", "world" ].joiner(",") ~ "]" ] , 
    "age" : [ 42.to!string ]
 ]);

"http://httpbin.org/get".getContent([
    "name" : [ "(" ~ [ "hello", "world" ].joiner(",") ~ ")" ] ,
    "age" : [ 42.to!string ]
]);

Массивы в качестве значений решают не задачу передачи списков значений, а задачу передачи нескольких пар с одинаковыми ключами.

У этого всего есть один большой недостаток — оно синхронное и блокирующее.
Мне, например, чаще нужен удобный синхронный клиент. Кому нужен асинхронный клиент спокойно использует vibe.d
Проблема в том, что в vibe.d используются коллбэки, а не промисы/async/await, поэтому асинхронный код в D громоздкий. Может быть, в будущем примут патчи для введения async/await в качестве ключевых слов самого языка.
Согласен на 100%! Для создания удобного неблокирующего клиента действительно требуется поддержка на более низком уровне, пусть даже не на уровне ключквых слов, но хотя-бы на уровне стандартной библиотеки.

Не нужны там никакие async/await-ы используйте fibers&streams.

Конечно не нужны! Ведь нужно всего-то создать новый класс, унаследовать его от Fiber-а, прописать ему call, потом создать экземпляр, и можно пользоваться! Правда, даже авторы D2 потом, видимо, поняли, что как-то чуточку громоздковато получается, и дали возможность передавать делегат для call параметром конструктора — и все равно это слишком много работы для того, что должно занимать один кейворд, да и к тому же в результате получаются semi-coroutine, как в Lua, а не полноценные awaitable.

Если бы в D был механизм декораторов, то async/await можно было бы реализовать и самостоятельно, но, увы, UDA не дают возможности менять определение или тело функции.
Одних fibers недостаточно, нужен еще event-loop (желательно из std) для неблокирующего io.

А о каких strams идёт речь?
нужен еще event-loop
У нас есть живой пример того, как можно сделать это элегантно — asyncio из Python.

Или еще лучше — сделайте наконец-то AST Macros из DIP-50! Тогда async/await можно будет просто реализовать без участия комитета одобрения D — и не только его. stm, linq, шаблонизаторы в стиле jinja или jade, aop… Возможности безграничны, и все это без жертв производительности. Однозначно нужен DIP-50.

А в чём проблема завернуть создание и запуск волокна в функцию?


unittest
{
    import core.time;
    import std.range;
    import jin.go;

    __gshared static string[] log;

    static void saying( string message )
    {
        foreach( _ ; 3.iota ) {
            sleep( 100.msecs );
            log ~= message;
        }
    }

    go!saying( "hello" );
    sleep( 50.msecs );
    saying( "world" );

    log.assertEq([ "hello" , "world" , "hello" , "world" , "hello" , "world" ]);
}

Что такое "semi-coroutine"? Почему вы считаете stackless сопрограммы, требующие костылей в виде async/await, единственно верными?

Я не смотрел еще эту библиотеку. Каждая go! выполняктся в отдельном thread или в отдельном Fiber? Во вторм случае — что произойдкт если несколько процедур зависнут в ожидании данных из сокеты?

Тогда рекомендую почитать эту статью: https://habrahabr.ru/post/280378/


go! запускает функцию в отдельном волокне.


Очевидно, эти процедуры никогда не проснутся и так и останутся висеть заблокированными. Если сработает таймаут, то он приведёт к пробуждению процедуры с возникновением в ней исключения.

Спасибо за статью, прочитал!

Если в отдельном волокне, то это хорошо, но не очень экономно. Мне больше нравится подход Fiber + неблокированный io, который, по идее, берёт лучшее от двух миров — «синхронный» код + скорость неблокированного io.

Ну так vibe.d как раз реализует неблокирующий io.

Я в курсе, но вот пишут что http-client он как-то неудобно реализует.

Вот уж не знаю, что там такого не удобного.


shared static this()
{
    runWorkerTask({
        logInfo( "Request IP..." );
        logInfo( "IP: %s" , "http://httpbin.org/ip".requestHTTP.readJson["origin"] );
    });

    runWorkerTask({
        logInfo( "Request UA..." );
        logInfo( "UA: %s" , "http://httpbin.org/user-agent".requestHTTP.readJson["user-agent"] );
    });

}

Request IP...
Request UA...
IP: "5.144.98.1"
UA: "vibe.d/0.7.28 (HTTPClient, +http://vibed.org/)"
В последнем релизе добавлена поддержка vibe.d сокет. Теперь можно использовать внутри vibe.d fibers c его неблокирующими сокетами
Только полноправные пользователи могут оставлять комментарии.
Войдите, пожалуйста.