DEV Community

Alireza Razinejad
Alireza Razinejad

Posted on

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 ❀

Top comments (0)