The asynchronous HTTP requests tutorial shows how to create async web requests in Go, C#, F#, Groovy, Python, Perl, PHP, Java, JavaScript.
last modified January 10, 2023
The asynchronous HTTP requests tutorial shows how to create async HTTP requests in Go, C#, F#, Groovy, Python, Perl, Java, JavaScript, and PHP.
Asynchronous requests do not block the client and allow us to generate HTTP requests more efficiently.
Rather than generating requests one by one, waiting for the current request to finish before executing next one, we execute all requests quickly and then wait for all of them to finish.
Go has goroutines for making asynchronous requests. A goroutine is a lightweight thread managed by the Go runtime.
main.go
package main
import ( “fmt” “io/ioutil” “log” “net/http” “regexp” “sync” )
func main() {
urls := []string{
"http://webcode.mse",
"https://example.com",
"http://httpbin.org",
"https://www.perl.org",
"https://www.php.net",
"https://www.python.org",
"https://code.visualstudio.com",
"https://clojure.org",
}
var wg sync.WaitGroup
for _, u := range urls {
wg.Add(1)
go func(url string) {
defer wg.Done()
content := doReq(url)
title := getTitle(content)
fmt.Println(title)
}(u)
}
wg.Wait()
}
func doReq(url string) (content string) {
resp, err := http.Get(url)
if err != nil {
log.Println(err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return
}
return string(body)
}
func getTitle(content string) (title string) {
re := regexp.MustCompile("<title>(.*)</title>")
parts := re.FindStringSubmatch(content)
if len(parts) > 0 {
return parts[1]
} else {
return "no title"
}
}
We make multiple asynchronous HTTP requests. We get the contents of the title tag of each of the web pages.
var wg sync.WaitGroup
WaitGroups are used to manage goroutines. It waits for a collection of goroutines to finish.
go func(url string) {
defer wg.Done()
content := doReq(url) title := getTitle(content) fmt.Println(title) }(u)
A goroutine is created with the go keyword.
$ go run async_req.go The Perl Programming Language - www.perl.org Welcome to Python.org Visual Studio Code - Code Editing. Redefined PHP: Hypertext Preprocessor Example Domain httpbin.org Clojure My html page
In C#, we use the HttpClient to generate asynchronous requests.
Program.cs
using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; using System.Text.RegularExpressions;
var urls = new string[] { “http://webcode.me”, “http://example.com”, “http://httpbin.org”, “https://ifconfig.me”, “http://termbin.com”, “https://github.com” };
var rx = new Regex(@"<title>\s*(.+?)\s*</title>", RegexOptions.Compiled);
using var client = new HttpClient();
var tasks = new List<Task<string>>();
foreach (var url in urls) { tasks.Add(client.GetStringAsync(url)); }
Task.WaitAll(tasks.ToArray());
var data = new List<string>();
foreach (var task in tasks) { data.Add(await task); }
foreach (var content in data) { var matches = rx.Matches(content);
foreach (var match in matches)
{
Console.WriteLine(match);
}
}
We download the given web pages asynchronously and print their HTML title tags.
tasks.Add(client.GetStringAsync(url));
The GetStringAsync sends a GET request to the specified url and returns the response body as a string in an asynchronous operation. It returns a new task; in C# a task represents an asynchronous operation.
Task.WaitAll(tasks.ToArray());
The Task.WaitAll waits for all of the provided tasks to complete execution.
data.Add(await task);
The await keywords unwraps the result value.
$ dotnet run <title>My html page</title> <title>Example Domain</title> <title>httpbin.org</title> <title>termbin.com - terminal pastebin</title> <title>GitHub: Where the world builds software ยท GitHub</title>
The following example uses HttpClient and task expressions to fetch website titles asynchronously.
async_req.fsx
open System.Net.Http open System.Text.RegularExpressions open System.Threading.Tasks
let fetchTitleAsync (url: string) =
task {
use client = new HttpClient()
let! html = client.GetStringAsync(url)
let pattern = "<title>\s*(.+?)\s*</title>"
let m = Regex.Match(html, pattern)
return m.Value
}
let sites = [| “http://webcode.me” “http://example.com” “https://bing.com” “http://httpbin.org” “https://ifconfig.me” “http://termbin.com” “https://github.com” |]
let titles = sites |> Array.map fetchTitleAsync |> Task.WhenAll |> Async.AwaitTask |> Async.RunSynchronously
titles |> Array.iter (fun title -> printfn $"%s{title}")
The example asynchronously retrieves the titles of the given urls.
Another solution uses the WebRequest to generate a request. Its GetResponseStream returns a response to a request as an asynchronous operation.
async_req2.fsx
open System.Net open System open System.Text.RegularExpressions
let fetchTitleAsync url =
async {
let req = WebRequest.Create(Uri(url))
use! resp = req.AsyncGetResponse()
use stream = resp.GetResponseStream()
use reader = new IO.StreamReader(stream)
let html = reader.ReadToEnd()
let pattern = "<title>\s*(.+?)\s*</title>"
let m = Regex.Match(html, pattern)
return m.Value
}
let sites = [ “http://webcode.me” “http://example.com” “https://bing.com” “http://httpbin.org” “https://ifconfig.me” “http://termbin.com” “https://github.com” ]
let titles = sites |> List.map fetchTitleAsync |> Async.Parallel |> Async.RunSynchronously
titles |> Array.iter (fun title -> printfn $"%s{title}")
The example asynchronously retrieves the titles of the given urls.
In Groovy, we use ExecutorService and HttpClient.
mul_async_req.gvy
import java.util.concurrent.Executors import java.util.concurrent.TimeUnit
import java.net.http.HttpClient import java.net.http.HttpRequest import java.net.http.HttpResponse
int nThreads = 30
def executor = Executors.newFixedThreadPool(nThreads)
def urls = [ “https://crunchify.com”, “https://yahoo.com”, “https://www.ebay.com”, “https://google.com”, “https://www.example.co”, “https://paypal.com”, “http://bing.com/”, “https://techcrunch.com/”, “http://mashable.com/”, “https://pro.crunchify.com/”, “https://wordpress.com/”, “https://wordpress.org/”, “https://example.com/”, “https://sjsu.edu/”, “https://ask.crunchify.com/”, “https://test.com.au/”, “https://www.wikipedia.org/”, “https://en.wikipedia.org” ]
for (String url in urls ) {
executor.execute(() -> {
worker(url)
// try {
// worker(url)
// } catch (Exception e) {
// e.printStackTrace()
// }
})
}
executor.shutdown()
executor.awaitTermination(30, TimeUnit.SECONDS) println(“finished”)
def worker(url) {
def client = HttpClient.newHttpClient()
def request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build()
HttpResponse<Void> res = client.send(request,
HttpResponse.BodyHandlers.discarding())
println "${url}: ${res.statusCode()}"
}
The example makes multiple asynchronous requests to URLs and prints their status codes.
$ groovy mul_async_req.gvy http://mashable.com/: 301 http://bing.com/: 301 https://paypal.com: 302 https://en.wikipedia.org: 301 https://paypal.com: 302 https://en.wikipedia.org: 301 https://en.wikipedia.org: 301 https://google.com: 301 https://example.com/: 200 https://example.com/: 200 https://yahoo.com: 301 https://test.com.au/: 301 https://wordpress.com/: 200 https://techcrunch.com/: 200 https://www.ebay.com: 200 https://ask.crunchify.com/: 200 https://pro.crunchify.com/: 200 https://sjsu.edu/: 200 finished
In Python, we use the httpx and asyncio modules.
async_req.py
#!/usr/bin/python
import httpx import asyncio
async def get_async(url): async with httpx.AsyncClient() as client: return await client.get(url)
urls = [‘http://webcode.me’, ‘https://httpbin.org/get’, ‘https://google.com’, ‘https://stackoverflow.com’, ‘https://github.com’]
async def launch(): resps = await asyncio.gather(*map(get_async, urls)) data = [resp.status_code for resp in resps]
for status_code in data:
print(status_code)
asyncio.run(launch())
The example makes asynchronous requests in Python. It prints the status code of all the provided urls.
./async_req.py 200 200 200 200 200
In Perl, we use the LWP module to generate requests and the Parallel::ForkManager module to make them asynchronous.
$ cpanm Parallel::ForkManager LWP
We install the modules with cpanm.
urls.txt
http://webcode.me https://example.com http://httpbin.org https://google.com https://www.perl.org https://fsharp.org https://clojure.org https://www.rust-lang.org https://golang.org https://www.python.org https://code.visualstudio.com https://ifconfig.me http://termbin.com https://github.com https://stackoverflow.com https://www.php.net/
The urls.txt contains a list of websites.
async_req.pl
#!/usr/bin/perl
use warnings; use 5.30.0; use Path::Tiny; use LWP::UserAgent; use Parallel::ForkManager;
my @urls = split “\n”, path(‘urls.txt’)->slurp_utf8;
my $pm = Parallel::ForkManager->new(4); my $ua = LWP::UserAgent->new; $ua->agent(‘Perl script’);
say “downloading “, scalar @urls, " files”;
my $dir = ‘files/’; mkdir $dir if not -d $dir;
foreach my $link (@urls) {
my $name = $1 if $link =~ m%https?://(.+)\.\w+%;
my $file_name = "$dir/$name" . '.txt';
$pm->start and next;
my $resp = $ua->get($link);
if ($resp->is_success) {
path($file_name)->spew_utf8($resp->decoded_content);
} else { warn $resp->status_line }
$pm->finish;
}
$pm->wait_all_children;
The example reads the urls.txt file and gets the links. It generates async requests to the given urls. The contents of the web pages are written to files.
$ ./async_req.pl downloading 15 files $ ls -1 files/ clojure.txt code.visualstudio.txt example.txt fsharp.txt github.txt golang.txt google.txt httpbin.txt ifconfig.txt stackoverflow.txt termbin.txt webcode.txt www.perl.txt www.python.txt www.rust-lang.txt
For JavaScript, we have chosen the axios module.
$ npm i axios
We install the axios module.
async_req.js
const axios = require(‘axios’);
async function makeRequests(urls) {
const fetchUrl = (url) => axios.get(url);
const promises = urls.map(fetchUrl);
let responses = await Promise.all(promises);
responses.forEach(resp => {
let msg = `${resp.config.url} -> ${resp.headers.server}: ${resp.status}`;
console.log(msg);
});
}
let urls = [ ‘http://webcode.me’, ‘https://example.com’, ‘http://httpbin.org’, ‘https://clojure.org’, ‘https://fsharp.org’, ‘https://symfony.com’, ‘https://www.perl.org’, ‘https://www.php.net’, ‘https://www.python.org’, ‘https://code.visualstudio.com’, ‘https://github.com’ ];
makeRequests(urls);
The example generates async requests to the given list of urls. It prints the web site’s url, server name, and status code.
const fetchUrl = (url) => axios.get(url);
The axios.get makes an async request and returns a promise.
let responses = await Promise.all(promises);
We collect all promises with Promise.All. The method resolves after all of the given promises have either fulfilled or rejected.
$ node async_req.js http://webcode.me -> nginx/1.6.2: 200 https://example.com -> ECS (dcb/7F83): 200 http://httpbin.org -> gunicorn/19.9.0: 200 https://clojure.org -> AmazonS3: 200 https://fsharp.org -> GitHub.com: 200 https://symfony.com -> cloudflare: 200 https://www.perl.org -> Combust/Plack (Perl): 200 https://www.php.net -> myracloud: 200 https://www.python.org -> nginx: 200 https://code.visualstudio.com -> Microsoft-IIS/10.0: 200 https://github.com -> GitHub.com: 200
The CompletableFuture a high-level API for asynchronous programming in Java.
com/zetcode/AsyncReqEx.java
package com.zetcode;
import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream;
import static java.util.stream.Collectors.toList;
public class AsyncReqEx {
public static void main(String[] args) {
List<URI> uris = Stream.of(
"https://www.google.com/",
"https://clojure.org",
"https://www.rust-lang.org",
"https://golang.org",
"https://www.python.org",
"https://code.visualstudio.com",
"https://ifconfig.me",
"http://termbin.com",
"https://www.github.com/"
).map(URI::create).collect(toList());
HttpClient httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
var futures = uris.stream()
.map(uri -> verifyUri(httpClient, uri))
.toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures).join();
}
private static CompletableFuture<Void> verifyUri(HttpClient httpClient,
URI uri) {
HttpRequest request = HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(5))
.uri(uri)
.build();
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding())
.thenApply(HttpResponse::statusCode)
.thenApply(statusCode -> statusCode == 200)
.exceptionally(ex -> false)
.thenAccept(valid -> {
if (valid) {
System.out.printf("[SUCCESS] Verified %s%n", uri);
} else {
System.out.printf("[FAILURE] Failed to verify%s%n", uri);
}
});
}
}
In the example, we have a list of urls. We check the status of the given web pages. The example uses HttpClient for making a web request and CompletableFuture for asynchronous execution.
[SUCCESS] Verified http://termbin.com [SUCCESS] Verified https://clojure.org [SUCCESS] Verified https://www.google.com/ [SUCCESS] Verified https://ifconfig.me [SUCCESS] Verified https://www.python.org [SUCCESS] Verified https://code.visualstudio.com [SUCCESS] Verified https://golang.org [SUCCESS] Verified https://www.rust-lang.org [SUCCESS] Verified https://www.github.com/
In PHP, we use the cURL library.
async_req.php
<?php
$urls = [ “http://webcode.me”, “https://example.com”, “http://httpbin.org”, “https://www.perl.org”, “https://www.php.net”, “https://www.python.org”, “https://code.visualstudio.com”, “https://ifconfig.me” ];
$options = [CURLOPT_HEADER => true, CURLOPT_NOBODY => true, CURLOPT_RETURNTRANSFER => true];
$mh = curl_multi_init(); $chs = [];
foreach ($urls as $url) {
$ch = curl_init($url);
curl_setopt_array($ch, $options);
curl_multi_add_handle($mh, $ch);
$chs[] = $ch;
}
$running = false;
do { curl_multi_exec($mh, $running); } while ($running);
foreach ($chs as $h) {
curl_multi_remove_handle($mh, $h);
}
curl_multi_close($mh);
foreach ($chs as $h) {
$status = curl_getinfo($h, CURLINFO_RESPONSE_CODE);
echo $status . "\n";
}
foreach ($chs as $h) {
echo "----------------------\n";
echo curl_multi_getcontent($h);
}
We print the status codes and headers of the requested web pages.
$ch = curl_init($url);
The curl_multi_init function creates a new multi handle, which allows the processing of multiple cURL handles asynchronously.
HTTP/1.1 200 OK Server: nginx/1.6.2 Date: Thu, 22 Jul 2021 13:14:22 GMT Content-Type: text/html Content-Length: 348 Last-Modified: Sat, 20 Jul 2019 11:49:25 GMT Connection: keep-alive ETag: “5d32ffc5-15c” Accept-Ranges: bytes
HTTP/2 200 content-encoding: gzip accept-ranges: bytes …
In this tutorial we have generated asynchronous web requests in Go, C#, F#, Python, Perl, Java, JavaScript, and PHP.