Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions quickstarts-js/Asynchronous_requests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* 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.
*/

/* Markdown (render)
# Gemini API: Asynchronous JavaScript requests

This notebook demonstrates how to make asynchronous and parallel requests using the Gemini API's JavaScript SDK with modern async/await syntax.

The examples here can run in environments like Node.js, or in browser consoles that support async functions. You can also manage concurrency using JavaScript's built-in event loop with `Promise.all()` or `for await...of` for efficient parallel execution.

## Setup
### Install SDK and set-up the client

### API Key Configuration

To ensure security, avoid hardcoding the API key in frontend code. Instead, set it as an environment variable on the server or local machine.

When using the Gemini API client libraries, the key will be automatically detected if set as either `GEMINI_API_KEY` or `GOOGLE_API_KEY`. If both are set, `GOOGLE_API_KEY` takes precedence.

For instructions on setting environment variables across different operating systems, refer to the official documentation: [Set API Key as Environment Variable](https://ai.google.dev/gemini-api/docs/api-key#set-api-env-var)

In code, the key can then be accessed as:

```js
ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
```
*/

// [CODE STARTS]
module = await import("https://esm.sh/@google/[email protected]");
GoogleGenAI = module.GoogleGenAI;
ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

MODEL_ID = "gemini-2.5-flash" // ["gemini-2.5-flash-lite", "gemini-2.5-flash", "gemini-2.5-pro"]
Comment on lines +43 to +47
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Variables module, GoogleGenAI, ai, and MODEL_ID are not declared with const, let, or var. This creates global variables, which is a bad practice and can lead to unexpected behavior. Please declare them with const as they are not reassigned.

const module = await import("https://esm.sh/@google/[email protected]");
const GoogleGenAI = module.GoogleGenAI;
const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });

const MODEL_ID = "gemini-2.5-flash" // ["gemini-2.5-flash-lite", "gemini-2.5-flash", "gemini-2.5-pro"]

// [CODE ENDS]

/* Markdown (render)
## Using local files

This simple example shows how can you use local files (presumed to load quickly) with the SDK's `async` API.
*/

// [CODE STARTS]
prompt = "Describe this image in just 3 words.";

imgFilenames = ["firefighter.jpg", "elephants.jpeg", "jetpack.jpg"];
imgDir = "https://storage.googleapis.com/generativeai-downloads/images/";
Comment on lines +57 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Variables prompt, imgFilenames, and imgDir are not declared with const, let, or var. This creates global variables, which is a bad practice. Please declare them with const as they are not reassigned.

const prompt = "Describe this image in just 3 words.";

const imgFilenames = ["firefighter.jpg", "elephants.jpeg", "jetpack.jpg"];
const imgDir = "https://storage.googleapis.com/generativeai-downloads/images/";

// [CODE ENDS]

/* Markdown (render)
Start by downloading the files locally.
*/

// [CODE STARTS]
imgFilenames = ["firefighter.jpg", "elephants.jpeg", "jetpack.jpg"];
imgDir = "https://storage.googleapis.com/generativeai-downloads/images/";
Comment on lines +68 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

These lines are a duplicate of lines 59-60 and are redundant. Please remove them to improve code clarity.


imageList = []
for (imgFilename of imgFilenames) {
imageBlob = await fetch(imgDir + imgFilename).then(res => res.blob());
imageBase64 = await new Promise((resolve) => {
reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(imageBlob);
});

imageList.push({ inlineData: { data: imageBase64, mimeType: "image/jpeg" } });
}
Comment on lines +71 to +81
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are several undeclared variables in this block (imageList, imgFilename, imageBlob, imageBase64, reader). Using variables without declaring them with const, let, or var creates global variables, which is a bad practice and can lead to bugs. Please declare them with const or let.

const imageList = []
for (const imgFilename of imgFilenames) {
  const imageBlob = await fetch(imgDir + imgFilename).then(res => res.blob());
  const imageBase64 = await new Promise((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result.split(',')[1]);
    reader.readAsDataURL(imageBlob);
  });

  imageList.push({ inlineData: { data: imageBase64, mimeType: "image/jpeg" } });
}

// [CODE ENDS]

/* Markdown (render)
This async function uses ai.models.generateContent to describe local images sequentially. Each await pauses for the API response, allowing the event loop to handle other tasks in between.
*/

// [CODE STARTS]
async function describeLocalImages() {
for (image of imageList) {
response = await ai.models.generateContent({
model: MODEL_ID,
contents: [prompt, image],
});
console.log(response.text);
}
}
Comment on lines +89 to +97
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The variables image and response inside the describeLocalImages function are not declared with const, let, or var. This creates global variables, which is a bad practice. Please declare them within their scope.

async function describeLocalImages() {
  for (const image of imageList) {
    const response = await ai.models.generateContent({
      model: MODEL_ID,
      contents: [prompt, image],
    });
    console.log(response.text);
  }
}


await describeLocalImages();
// [CODE ENDS]

/* Output Sample

Boy, cat, tree.

Jungle elephant family

Jetpack Backpack Sketch

*/

/* Markdown (render)
## Downloading images asynchronously and in parallel

This example shows a more real-world case where images are downloaded from an external source using the async fetch() function in JavaScript, and each image is processed in parallel.
*/

// [CODE STARTS]
async function downloadImage(imgUrl) {
imageBlob = await fetch(imgUrl).then(res => res.blob());
imageBase64 = await new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(imageBlob);
});

return { inlineData: { data: imageBase64, mimeType: "image/jpeg" } };
}
Comment on lines +119 to +128
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The variables imageBlob and imageBase64 inside the downloadImage function are not declared with const, let, or var. This creates global variables, which is a bad practice. Please declare them with const.

async function downloadImage(imgUrl) {
  const imageBlob = await fetch(imgUrl).then(res => res.blob());
  const imageBase64 = await new Promise((resolve) => {
    const reader = new FileReader();
    reader.onloadend = () => resolve(reader.result.split(',')[1]);
    reader.readAsDataURL(imageBlob);
  });

  return { inlineData: { data: imageBase64, mimeType: "image/jpeg" } };
}


async function processImage(imgPromise) {
response = await ai.models.generateContent({
model: MODEL_ID,
contents: [prompt, await imgPromise],
});
return response.text;
}
Comment on lines +130 to +136
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The response variable inside the processImage function is not declared with const, let, or var. This creates a global variable, which is a bad practice. Please declare it with const.

async function processImage(imgPromise) {
  const response = await ai.models.generateContent({
    model: MODEL_ID,
    contents: [prompt, await imgPromise],
  });
  return response.text;
}


responsePromises = [];

for (imgFilename of imgFilenames) {
const imgPromise = downloadImage(imgDir + imgFilename);
const textPromise = processImage(imgPromise);
responsePromises.push(textPromise);
}

console.log(`Download and content generation queued for ${responsePromises.length} images.`);

for await (response of responsePromises) {
console.log(await response);
}
Comment on lines +138 to +150
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There are a few issues in this block:

  1. Variables responsePromises, imgFilename, and response are not declared with const, let, or var. This creates global variables, which is a bad practice.
  2. On line 149, await response is unnecessary. The for await...of loop already awaits each promise from responsePromises, so response is a resolved string, not a promise. Awaiting a non-promise value is a no-op but it is confusing and should be removed.

Please declare the variables and remove the redundant await.

const responsePromises = [];

for (const imgFilename of imgFilenames) {
  const imgPromise = downloadImage(imgDir + imgFilename);
  const textPromise = processImage(imgPromise);
  responsePromises.push(textPromise);
}

console.log(`Download and content generation queued for ${responsePromises.length} images.`);

for await (const response of responsePromises) {
  console.log(response);
}

// [CODE ENDS]

/* Output Sample

Download and content generation queued for 3 images.

Boy, cat, tree.

Wild Elephant Family

Jetpack concept sketch.

*/

/* Markdown (render)
In the above example, an async function is created for each image that both downloads and then summarizes the image. These async tasks are executed in the final step, when their Promises are awaited in sequence. To start them as early as possible without blocking other work, you could wrap the downloadImage call in Promise.resolve() or trigger it immediately, but in this example, execution is deferred to keep the creation and execution logic separate.
*/

/* Markdown (render)
## Next Steps

* Explore the [`@google/genai`](https://www.npmjs.com/package/@google/genai) JavaScript SDK for detailed usage and API documentation.
* Learn more about JavaScript's [`async/await`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) and [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) for efficient parallel execution.
*/
1 change: 1 addition & 0 deletions quickstarts-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ Stay tuned, more JavaScript notebooks are on the way!
| File API | Learn how to upload, use, retrieve, and delete files (text, image, audio, code) with the Gemini File API for multimodal prompts. | File upload, multimodal prompts, text/code/media files | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/file_api?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [File_API.js](./File_API.js) |
| Audio | Demonstrates how to use audio files with Gemini: upload, prompt, summarize, transcribe, and analyze audio and YouTube content. | Audio file upload, inline audio, transcription, YouTube analysis | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/audio?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Audio.js](./Audio.js) |
| Get Started LearnLM | Explore LearnLM, an experimental model for AI tutoring, with examples of system instructions for test prep, concept teaching, learning activities, and homework help. | AI tutoring, system instructions, adaptive learning, education | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](https://aistudio.google.com/apps/bundled/get_started_learnlm?showPreview=true) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Get_started_LearnLM.js](./Get_started_LearnLM.js) |
| Asynchronous Requests | Learn how to make asynchronous and parallel requests using the Gemini JavaScript SDK with async/await, Promise.all, and file handling examples. | Async/await, parallel requests, file input, event loop concurrency | [![Open in AI Studio](https://storage.googleapis.com/generativeai-downloads/images/Open_in_AIStudio.svg)](#) | <img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/javascript/javascript-original.svg" alt="JS" width="20"/> [Asynchronous_requests.js](./Asynchronous_requests.js) |