Merchant API code sample to insert product input asynchronously.
Java
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package
shopping.merchant.samples.products.v1
;
import
com.google.api.core.ApiFuture
;
import
com.google.api.core.ApiFutureCallback
;
import
com.google.api.core.ApiFutures
;
import
com.google.api.gax.core.FixedCredentialsProvider
;
import
com.google.auth.oauth2.GoogleCredentials
;
import
com.google.common.util.concurrent.MoreExecutors
;
import
com.google.shopping.merchant.products.v1.Availability
;
import
com.google.shopping.merchant.products.v1.Condition
;
import
com.google.shopping.merchant.products.v1.InsertProductInputRequest
;
import
com.google.shopping.merchant.products.v1.ProductAttributes
;
import
com.google.shopping.merchant.products.v1.ProductInput
;
import
com.google.shopping.merchant.products.v1.ProductInputsServiceClient
;
import
com.google.shopping.merchant.products.v1.ProductInputsServiceSettings
;
import
com.google.shopping.merchant.products.v1.Shipping
;
import
com.google.shopping.type.Price
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Random
;
import
java.util.stream.Collectors
;
import
shopping.merchant.samples.utils.Authenticator
;
import
shopping.merchant.samples.utils.Config
;
/** This class demonstrates how to insert a product input */
public
class
InsertProductInputAsyncSample
{
private
static
String
getParent
(
String
accountId
)
{
return
String
.
format
(
"accounts/%s"
,
accountId
);
}
private
static
String
generateRandomString
()
{
String
characters
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
;
Random
random
=
new
Random
();
StringBuilder
sb
=
new
StringBuilder
(
8
);
for
(
int
i
=
0
;
i
<
8
;
i
++
)
{
sb
.
append
(
characters
.
charAt
(
random
.
nextInt
(
characters
.
length
())));
}
return
sb
.
toString
();
}
private
static
ProductInput
createRandomProduct
()
{
Price
price
=
Price
.
newBuilder
().
setAmountMicros
(
33_450_000
).
setCurrencyCode
(
"USD"
).
build
();
Shipping
shipping
=
Shipping
.
newBuilder
().
setPrice
(
price
).
setCountry
(
"GB"
).
setService
(
"1st class post"
).
build
();
Shipping
shipping2
=
Shipping
.
newBuilder
().
setPrice
(
price
).
setCountry
(
"FR"
).
setService
(
"1st class post"
).
build
();
ProductAttributes
attributes
=
ProductAttributes
.
newBuilder
()
.
setTitle
(
"A Tale of Two Cities"
)
.
setDescription
(
"A classic novel about the French Revolution"
)
.
setLink
(
"https://exampleWebsite.com/tale-of-two-cities.html"
)
.
setImageLink
(
"https://exampleWebsite.com/tale-of-two-cities.jpg"
)
.
setAvailability
(
Availability
.
IN_STOCK
)
.
setCondition
(
Condition
.
NEW
)
.
setGoogleProductCategory
(
"Media > Books"
)
.
addGtins
(
"9780007350896"
)
.
addShipping
(
shipping
)
.
addShipping
(
shipping2
)
.
build
();
return
ProductInput
.
newBuilder
()
.
setContentLanguage
(
"en"
)
.
setFeedLabel
(
"CH"
)
.
setOfferId
(
generateRandomString
())
.
setProductAttributes
(
attributes
)
.
build
();
}
public
static
void
asyncInsertProductInput
(
Config
config
,
String
dataSource
)
throws
Exception
{
// Obtains OAuth token based on the user's configuration.
GoogleCredentials
credential
=
new
Authenticator
().
authenticate
();
// Creates service settings using the credentials retrieved above.
ProductInputsServiceSettings
productInputsServiceSettings
=
ProductInputsServiceSettings
.
newBuilder
()
.
setCredentialsProvider
(
FixedCredentialsProvider
.
create
(
credential
))
.
build
();
// Creates parent to identify where to insert the product.
String
parent
=
getParent
(
config
.
getAccountId
().
toString
());
// Calls the API and catches and prints any network failures/errors.
try
(
ProductInputsServiceClient
productInputsServiceClient
=
ProductInputsServiceClient
.
create
(
productInputsServiceSettings
))
{
// Creates five insert product input requests with random product IDs.
List<InsertProductInputRequest>
requests
=
new
ArrayList
<> (
5
);
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
InsertProductInputRequest
request
=
InsertProductInputRequest
.
newBuilder
()
.
setParent
(
parent
)
// You can only insert products into datasource types of Input "API", and of Type
// "Primary" or "Supplemental."
// This field takes the `name` field of the datasource.
.
setDataSource
(
dataSource
)
// If this product is already owned by another datasource, when re-inserting, the
// new datasource will take ownership of the product.
.
setProductInput
(
createRandomProduct
())
.
build
();
requests
.
add
(
request
);
}
System
.
out
.
println
(
"Sending insert product input requests"
);
List<ApiFuture<ProductInput>
>
futures
=
requests
.
stream
()
.
map
(
request
-
>
productInputsServiceClient
.
insertProductInputCallable
().
futureCall
(
request
))
.
collect
(
Collectors
.
toList
());
// Creates callback to handle the responses when all are ready.
ApiFuture<List<ProductInput>
>
responses
=
ApiFutures
.
allAsList
(
futures
);
ApiFutures
.
addCallback
(
responses
,
new
ApiFutureCallback<List<ProductInput>
> ()
{
@Override
public
void
onSuccess
(
List<ProductInput>
results
)
{
System
.
out
.
println
(
"Inserted products below"
);
System
.
out
.
println
(
results
);
}
@Override
public
void
onFailure
(
Throwable
throwable
)
{
System
.
out
.
println
(
throwable
);
}
},
MoreExecutors
.
directExecutor
());
}
catch
(
Exception
e
)
{
System
.
out
.
println
(
e
);
}
}
public
static
void
main
(
String
[]
args
)
throws
Exception
{
Config
config
=
Config
.
load
();
// Identifies the data source that will own the product input.
String
dataSource
=
"accounts/"
+
config
.
getAccountId
()
+
"/dataSources/{datasourceId}"
;
asyncInsertProductInput
(
config
,
dataSource
);
}
}
Node.js
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
'use strict'
;
const
fs
=
require
(
'fs'
);
const
authUtils
=
require
(
'../../authentication/authenticate.js'
);
const
{
ProductInputsServiceClient
,
}
=
require
(
'@google-shopping/products'
).
v1
;
const
{
protos
,
}
=
require
(
'@google-shopping/products'
);
const
Availability
=
protos
.
google
.
shopping
.
merchant
.
products
.
v1
.
Availability
;
const
Condition
=
protos
.
google
.
shopping
.
merchant
.
products
.
v1
.
Condition
;
/**
* This class demonstrates how to insert a product input asynchronously.
*/
/**
* Helper function to generate a random string for offerId
* @returns {string} A sample offerId.
*/
function
generateRandomString
()
{
const
characters
=
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
;
let
result
=
''
;
const
length
=
8
;
for
(
let
i
=
0
;
i
<
length
;
i
++
)
{
result
+=
characters
.
charAt
(
Math
.
floor
(
Math
.
random
()
*
characters
.
length
));
}
return
result
;
}
/**
* Helper function to create a sample ProductInput object
* @returns {!object} A sample ProductInput object.
*/
function
createRandomProduct
()
{
const
shippingPrice
=
{
amountMicros
:
3000000
,
// 3 USD
currencyCode
:
'USD'
,
};
const
price
=
{
amountMicros
:
33450000
,
// 33.45 USD
currency_code
:
'USD'
,
};
const
shipping
=
{
price
:
shippingPrice
,
country
:
'GB'
,
service
:
'1st class post'
,
};
const
shipping2
=
{
price
:
shippingPrice
,
country
:
'FR'
,
service
:
'1st class post'
,
};
const
attributes
=
{
title
:
'A Tale of Two Cities'
,
description
:
'A classic novel about the French Revolution'
,
link
:
'https://exampleWebsite.com/tale-of-two-cities.html'
,
image_link
:
'https://exampleWebsite.com/tale-of-two-cities.jpg'
,
availability
:
Availability
.
IN_STOCK
,
condition
:
Condition
.
NEW
,
google_product_category
:
'Media > Books'
,
gtins
:
[
'9780007350896'
],
shipping
:
[
shipping
,
shipping2
],
price
:
price
,
};
// Construct the ProductInput object
const
productInput
=
{
contentLanguage
:
'en'
,
feedLabel
:
'CH'
,
offerId
:
generateRandomString
(),
productAttributes
:
attributes
,
};
return
productInput
;
}
/**
* Inserts multiple product inputs asynchronously.
* @param {!object} config - Configuration object.
* @param {string} dataSource - The data source name.
*/
async
function
asyncInsertProductInput
(
config
,
dataSource
)
{
// Read merchant_id from the configuration file.
const
merchantInfo
=
JSON
.
parse
(
fs
.
readFileSync
(
config
.
merchantInfoFile
,
'utf8'
)
);
const
merchantId
=
merchantInfo
.
merchantId
;
// Construct the parent resource name string.
const
parent
=
`accounts/
${
merchantId
}
`
;
// Get OAuth2 credentials.
const
authClient
=
await
authUtils
.
getOrGenerateUserCredentials
();
// Create client options with authentication.
const
options
=
{
authClient
:
authClient
};
// Create the ProductInputsServiceClient.
const
productInputsServiceClient
=
new
ProductInputsServiceClient
(
options
);
// Create five insert product input requests with random product details.
const
requests
=
[];
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
const
request
=
{
parent
:
parent
,
// You can only insert products into datasource types of Input "API" and "FILE", and
// of Type "Primary" or "Supplemental."
// This field takes the `name` field of the datasource, e.g.,
// accounts/123/dataSources/456
dataSource
:
dataSource
,
// If this product is already owned by another datasource, when re-inserting, the
// new datasource will take ownership of the product.
productInput
:
createRandomProduct
(),
};
requests
.
push
(
request
);
}
console
.
log
(
'Sending insert product input requests...'
);
// Create an array of promises by calling the insertProductInput method for each request.
const
insertPromises
=
requests
.
map
(
request
=
>
productInputsServiceClient
.
insertProductInput
(
request
)
);
// Wait for all insert operations to complete.
// Promise.all returns an array of results, where each result is the response
// from the corresponding insertProductInput call (which is the inserted ProductInput).
// The response from insertProductInput is an array where the first element is the ProductInput.
const
results
=
await
Promise
.
all
(
insertPromises
);
const
insertedProducts
=
results
.
map
(
result
=
>
result
[
0
]);
// Extract ProductInput from each response array
console
.
log
(
'Inserted products below:'
);
console
.
log
(
JSON
.
stringify
(
insertedProducts
,
null
,
2
));
}
/**
* Main function to call the async insert product input method.
*/
async
function
main
()
{
// Get configuration settings.
const
config
=
authUtils
.
getConfig
();
// Define the data source ID. Replace {datasourceId} with your actual data source ID.
// The format is accounts/{account_id}/dataSources/{datasource_id}.
const
merchantInfo
=
JSON
.
parse
(
fs
.
readFileSync
(
config
.
merchantInfoFile
,
'utf8'
)
);
const
merchantId
=
merchantInfo
.
merchantId
;
const
dataSource
=
`accounts/
${
merchantId
}
/dataSources/{datasourceId}`
;
// Replace {datasourceId}
try
{
await
asyncInsertProductInput
(
config
,
dataSource
);
}
catch
(
error
)
{
console
.
error
(
`An error occurred:
${
error
.
message
||
error
}
`
);
// Log details if available (e.g., for gRPC errors)
if
(
error
.
details
)
{
console
.
error
(
`Details:
${
error
.
details
}
`
);
}
}
}
main
();
PHP
< ?php
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
require_once __DIR__ . '/../../../vendor/autoload.php';
require_once __DIR__ . '/../../Authentication/Authentication.php';
require_once __DIR__ . '/../../Authentication/Config.php';
use Google\ApiCore\ApiException;
use Google\Shopping\Merchant\Products\V1\Availability;
use Google\Shopping\Merchant\Products\V1\Condition;
use Google\Shopping\Merchant\Products\V1\ProductAttributes;
use Google\Shopping\Merchant\Products\V1\InsertProductInputRequest;
use Google\Shopping\Merchant\Products\V1\ProductInput;
use Google\Shopping\Merchant\Products\V1\Client\ProductInputsServiceClient;
use Google\Shopping\Merchant\Products\V1\Shipping;
use Google\Shopping\Type\Price;
use React\EventLoop\Loop;
use React\Promise\Promise;
use function React\Promise\all;
/**
* This class demonstrates how to insert multiple product inputs asynchronously.
*/
class InsertProductInputAsyncSample
{
/**
* A helper function to create the parent string for product input operations.
*
* @param string $accountId The Merchant Center account ID.
* @return string The parent resource name format: `accounts/{account_id}`.
*/
private static function getParent(string $accountId): string
{
return sprintf("accounts/%s", $accountId);
}
/**
* Generates a random string of 8 characters.
*
* @return string A random alphanumeric string.
*/
private static function generateRandomString(): string
{
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$randomString = '';
$charactersLength = strlen($characters);
for ($i = 0; $i < 8; $i++) {
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
/**
* Creates a ProductInput object with randomized offer ID and sample attributes.
*
* @return ProductInput A new ProductInput object.
*/
private static function createRandomProduct(): ProductInput
{
// Create a price object for shipping. Amount is in micros.
// e.g., 33,450,000 micros = $33.45 USD
$price = new Price([
'amount_micros' => 33450000,
'currency_code' => 'USD'
]);
// Create shipping details.
$shipping = new Shipping([
'price' => $price,
'country' => 'GB',
'service' => '1st class post'
]);
$shipping2 = new Shipping([
'price' => $price,
'country' => 'FR',
'service' => '1st class post'
]);
// Create product attributes.
$attributes = new ProductAttributes([
'title' => 'A Tale of Two Cities',
'description' => 'A classic novel about the French Revolution',
'link' => 'https://exampleWebsite.com/tale-of-two-cities.html',
'image_link' => 'https://exampleWebsite.com/tale-of-two-cities.jpg',
'availability' => Availability::IN_STOCK,
'condition' => Condition::PBNEW,
'google_product_category' => 'Media > Books',
'gtins' => ['9780007350896'],
'shipping' => [$shipping, $shipping2]
]);
// Create the product input object.
return new ProductInput([
'content_language' => 'en',
'feed_label' => 'LABEL',
'offer_id' => self::generateRandomString(), // Random offer ID for uniqueness
'product_attributes' => $attributes
]);
}
/**
* Inserts multiple product inputs into the specified account and data source asynchronously.
*
* @param array $config Authentication and account configuration.
* @param string $dataSource The target data source name.
* Format: `accounts/{account}/dataSources/{datasource}`.
* @return void
*/
public static function insertProductInputAsyncSample(array $config, string $dataSource): void
{
// Fetches OAuth2 credentials for making API calls.
$credentials = Authentication::useServiceAccountOrTokenFile();
// Prepares client options with the fetched credentials.
$options = ['credentials' => $credentials];
// Initializes the ProductInputsServiceAsyncClient.
// This is the key for asynchronous operations.
$productInputsServiceAsyncClient = new ProductInputsServiceClient($options);
// Constructs the parent resource string.
$parent = self::getParent($config['accountId']);
$promises = [];
$insertedProductInputs = [];
print "Sending insert product input requests asynchronously...\n";
// Create and send 5 insert product input requests asynchronously.
for ($i = 0; $i < 5; $i++) {
$productInput = self::createRandomProduct();
// Create the request object.
$request = new InsertProductInputRequest([
'parent' => $parent,
'data_source' => $dataSource,
'product_input' => $productInput
]);
// Make the asynchronous API call. This returns a Promise.
$promise = $productInputsServiceAsyncClient->insertProductInputAsync($request);
// Attach success and error handlers to the promise.
$promise->then(
function (ProductInput $response) use (&$insertedProductInputs) {
// This callback is executed when the promise resolves (success).
$insertedProductInputs[] = $response;
print "Successfully inserted product with offer ID: " . $response->getOfferId() . "\n";
},
function (ApiException $e) {
// This callback is executed if the promise rejects (failure).
echo "ApiException occurred for one of the requests:\n";
echo $e;
}
);
$promises[] = $promise;
}
// Wait for all promises to settle (either resolve or reject).
// Reduce::all() creates a single promise that resolves when all input promises resolve.
// If any promise rejects, the combined promise will reject.
all($promises)->then(
function () use (&$insertedProductInputs) {
print "All asynchronous requests have completed.\n";
// Print details of all successfully inserted products.
print "Inserted products below\n";
foreach ($insertedProductInputs as $p) {
print_r($p);
}
},
function ($reason) {
// This block is executed if any promise in the array rejects.
echo "One or more asynchronous requests failed.\n";
if ($reason instanceof ApiException) {
echo "API Exception: " . $reason->getMessage() . "\n";
} else {
echo "Error: " . $reason . "\n";
}
}
)->always(function () use ($productInputsServiceAsyncClient) {
// This 'always' callback ensures the client is closed after all promises settle.
$productInputsServiceAsyncClient->close();
});
// Run the event loop. This is crucial for asynchronous operations to execute.
// The script will block here until all promises are resolved/rejected or the loop is stopped.
Loop::run();
}
/**
* Executes the sample code to insert multiple product inputs.
*
* @return void
*/
public function callSample(): void
{
$config = Config::generateConfig();
// Define the data source that will own the product inputs.
// IMPORTANT: Replace `<DATA_SOURCE_ID>` with your actual data source ID.
$dataSourceId = '<DATA_SOURCE_ID>';
$dataSourceName = sprintf(
"accounts/%s/dataSources/%s",
$config['accountId'], $dataSourceId
);
// Call the method to insert multiple product inputs asynchronously.
self::insertProductInputAsyncSample($config, $dataSourceName);
}
}
$sample = new InsertProductInputAsyncSample();
$sample->callSample();
Python
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A module to insert product inputs asynchronously."""
import
asyncio
import
functools
import
random
import
string
from
examples.authentication
import
configuration
from
examples.authentication
import
generate_user_credentials
from
google.shopping.merchant_products_v1
import
Availability
from
google.shopping.merchant_products_v1
import
Condition
from
google.shopping.merchant_products_v1
import
InsertProductInputRequest
from
google.shopping.merchant_products_v1
import
ProductAttributes
from
google.shopping.merchant_products_v1
import
ProductInput
from
google.shopping.merchant_products_v1
import
ProductInputsServiceAsyncClient
from
google.shopping.merchant_products_v1
import
Shipping
from
google.shopping.type
import
Price
# Read merchant account information from the configuration file.
_ACCOUNT_ID
=
configuration
.
Configuration
()
.
read_merchant_info
()
# The parent account for the product input.
# Format: accounts/{account}
_PARENT
=
f
"accounts/
{
_ACCOUNT_ID
}
"
def
_generate_random_string
(
length
:
int
=
8
)
-
> str
:
"""Generates a random string of a given length."""
characters
=
string
.
ascii_letters
+
string
.
digits
return
""
.
join
(
random
.
choice
(
characters
)
for
_
in
range
(
length
))
def
_create_random_product
()
-
> ProductInput
:
"""Creates a ProductInput with random elements and predefined attributes."""
price
=
Price
(
amount_micros
=
33450000
,
currency_code
=
"USD"
)
shipping1
=
Shipping
(
price
=
price
,
country
=
"GB"
,
service
=
"1st class post"
)
shipping2
=
Shipping
(
price
=
price
,
country
=
"FR"
,
service
=
"1st class post"
)
attributes
=
ProductAttributes
(
title
=
"Async - A Tale of Two Cities"
,
description
=
"A classic novel about the French Revolution"
,
link
=
"https://exampleWebsite.com/tale-of-two-cities.html"
,
image_link
=
"https://exampleWebsite.com/tale-of-two-cities.jpg"
,
availability
=
Availability
.
IN_STOCK
,
condition
=
Condition
.
NEW
,
google_product_category
=
"Media > Books"
,
gtins
=
[
"9780007350896"
],
shipping
=
[
shipping1
,
shipping2
],
)
return
ProductInput
(
content_language
=
"en"
,
feed_label
=
"US"
,
offer_id
=
_generate_random_string
(),
product_attributes
=
attributes
,
)
def
print_product_input
(
i
,
task
):
print
(
"Inserted ProductInput number: "
,
i
)
# task.result() contains the response from async_insert_product_input
print
(
task
.
result
())
async
def
async_insert_product_input
(
client
:
ProductInputsServiceAsyncClient
,
request
:
InsertProductInputRequest
):
"""Inserts product inputs.
Args:
client: The ProductInputsServiceAsyncClient to use.
request: The InsertProductInputRequest to send.
Returns:
The response from the insert_produc_input request.
"""
print
(
"Sending insert product input requests"
)
try
:
response
=
await
client
.
insert_product_input
(
request
=
request
)
# The response is an async corouting inserting the ProductInput.
return
response
except
RuntimeError
as
e
:
# Catch and print any exceptions that occur during the API calls.
print
(
e
)
async
def
main
():
# The ID of the data source that will own the product input.
# This is a placeholder and should be replaced with an actual data source ID.
datasource_id
=
"<INSERT_DATA_SOURCE_ID_HERE>"
data_source_name
=
f
"accounts/
{
_ACCOUNT_ID
}
/dataSources/
{
datasource_id
}
"
# Gets OAuth Credentials.
credentials
=
generate_user_credentials
.
main
()
# Creates a ProductInputsServiceClient.
client
=
ProductInputsServiceAsyncClient
(
credentials
=
credentials
)
tasks
=
[]
for
i
in
range
(
5
):
product_input
=
_create_random_product
()
request
=
InsertProductInputRequest
(
parent
=
_PARENT
,
data_source
=
data_source_name
,
product_input
=
product_input
,
)
# Create the async task
insert_product_task
=
asyncio
.
create_task
(
async_insert_product_input
(
client
,
request
)
)
# Add the callback
callback_function
=
functools
.
partial
(
print_product_input
,
i
)
insert_product_task
.
add_done_callback
(
callback_function
)
# Add the task to our list
tasks
.
append
(
insert_product_task
)
# Await all tasks to complete concurrently
# The print_product_input callback will be called for each as it finishes
await
asyncio
.
gather
(
*
tasks
)
if
__name__
==
"__main__"
:
asyncio
.
run
(
main
())