DEV Community

Alireza Razinejad
Alireza Razinejad

Posted on

5 2

Let's create our own asynchronous iterator object

Hey guys ! ๐Ÿ‘‹
Hope you are doing well ๐Ÿคž

And Happy Thanks giving ๐ŸŽ‰๐Ÿฆƒ

Yesterday, just wrote a short article about Create our own iterable in JavaScript, please make sure to check last sniped code there, because we are going to update it to asynchronous object here ๐Ÿ™

Today we are going to make that simple interable object more useful ๐Ÿ˜Š

We have some operators like forkJoin from rxjs, that will let us to complete array of observable objects ( maybe it's good subject to write short article about ๐Ÿค”)

That is really use full feature when we are going to fetch multiple data from multiple sources ๐Ÿ“Œ

Update is simple, first let's see how our iterable object look's like

const ourOwnIterable = {
    value: [1, 2, 3, 4, 5],
    index: 0,
    [Symbol.iterator]() {
        return {
            next: () => {
                if(this.value.length === this.index) {
                    return {
                        value: null,
                        done: true
                    }
                }
                this.index++;
                return {
                    value: this.value[this.index - 1],
                    done: false
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

And we was able to go throw values using for-of loop like so

for (const el of ourOwnIterable) {
 console.log(el)
}
Enter fullscreen mode Exit fullscreen mode

Let's clear our scenario, we are going to have some URLs to retrieve some data from and have the ability to go throw them one by one and see the result for each one of them ๐Ÿ˜

First we are going to use URLs instead of values

const ourOwnIterable = {
    urls: [],
    ...
Enter fullscreen mode Exit fullscreen mode

Then we are going to use asyncIterator method of Symbol instead of iterator

....
 [Symbol.asyncIterator]() {
        return {
        ....
Enter fullscreen mode Exit fullscreen mode

As we are going to use fetch for calling our URLs, and await operator, we need to update our next() function, and decorate it with async prefix

...
return {
 next: async () => {
...
Enter fullscreen mode Exit fullscreen mode

Now we are ready to implement our logic to retrieve, extract and return data from URLs

next: async () => {
    if (this.urls.length === this.index) {
        return {
            value: null,
            done: true
        }
    }
    this.index++;
    const fetchedResult = await fetch(this.urls[this.index - 1]);
    const extractedData = await fetchedResult.json();
    return {
        value: extractedData,
        done: false
    }
}
Enter fullscreen mode Exit fullscreen mode

It can be good practice to put our fetch solution inside try-catch to have some error handling

try {
    const fetchedResult = await fetch(this.urls[this.index - 1]);
    const extractedData = await fetchedResult.json();

    return {
        value: extractedData,
        done: false
    }
} catch (e) {
    return {
        value: {
            url: this.urls[this.index - 1],
            error_message: `Got error ${e.message}`
        },
        done: false
    }
}
Enter fullscreen mode Exit fullscreen mode

Now our iterable object is ready to use with for-of loop ๐Ÿ˜„

for await (const res of ourOwnIterable) {
        console.log(res);
    }
Enter fullscreen mode Exit fullscreen mode

Ok, let's put this for-loop inside some async function, pass some URLs and see what will happen ! ๐Ÿคช

async function fetchAllUrls(urls) {
    ourOwnIterable.urls = urls;
    for await (const res of ourOwnIterable) {
        console.log(res);
    }
}

fetchAllUrls([
    'https://jsonplaceholder.typicode.com/todos/1',
    'https://jsonplaceholder.typicode.com/todos/2',
    'https://jsonplaceholder.typicode.com/todos/3'
]);
Enter fullscreen mode Exit fullscreen mode

To see the results we need to have some HTML document, as we are using fetch() method (HTML API ๐Ÿค”)

The desire out put will be something like this

Object { userId: 1, id: 1, title: "delectus aut autem", completed: false }
Object { userId: 1, id: 2, title: "quis ut nam facilis et officia qui", completed: false }
Object { userId: 1, id: 3, title: "fugiat veniam minus", completed: false }
Enter fullscreen mode Exit fullscreen mode

And that's it ๐Ÿคธโ€โ™‚๏ธ

Now we have our own iterable object that can fetch array of URLs one by one with beautiful error handler

The final full script will be like this

const ourOwnIterable = {
    urls: [],
    index: 0,
    /**
     * 
     * @returns {{
     * next: (function(): Promise<{value: null, done: boolean}
     * |{value: any, done: boolean}
     * |{value: {error_message: string, url: *}, done: boolean}
     * |undefined>)}}
     */
    [Symbol.asyncIterator]() {
        return {
            next: async () => {
                if (this.urls.length === this.index) {
                    return {
                        value: null,
                        done: true
                    }
                }
                this.index++;
                try {
                    const fetchRes = await fetch(this.urls[this.index - 1]);
                    const extractedData = await fetchRes.json();

                    return {
                        value: extractedData,
                        done: false
                    }
                } catch (e) {
                    return {
                        value: {
                            url: this.urls[this.index - 1],
                            error_message: `Got error ${e.message}`
                        },
                        done: false
                    }
                }
            }
        }
    }
}

/**
 * 
 * @param urls
 * @returns {Promise<void>}
 */
async function fetchAllUrls(urls) {
    ourOwnIterable.urls = urls;
    for await (const res of ourOwnIterable) {
        console.log(res);
    }
}

fetchAllUrls([
    'https://jsonplaceholder.typicode.com/todos/1',
    'https://jsonplaceholder.typicode.com/todos/2',
    'https://jsonplaceholder.typicode.com/todos/3'
]);

Enter fullscreen mode Exit fullscreen mode

Thank you so much for your time ๐Ÿค

Hope you enjoyed โค

Image of Timescale

๐Ÿš€ pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applicationsโ€”without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post โ†’

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more