0

I have to add a list of data structures to a js object on an HTML page. I have a list of data structures on the server side each identified by a data-key. A js function loops the list of data-keys the function gets each structure one at a time from a async_cache using the corresponding data-key. There may be multiples of same data-keys in the list

  1. if the async_cache doesn't have the data-structure by that data-key it async-fetches and adds it to the cache.
  2. if the async_cache has the data-structure by that data-key it returns it right away.
  3. if the async_cache has already requested a fetch for a data-key and before the data arrives has another request for the same data-key it must not duplicate the request but wait until the data arrives.

I have constructed the following code so far (as an example for discussion). The actual environment can't be shared.

<?php 
    switch($_GET['--action'] ?? false){
        case 'data-structure':{
            $data_group = [
                'data-01' => ['a' => 'info-01', 'b' => 'extra-01'],
                'data-02' => ['a' => 'info-02', 'b' => 'extra-02'],
                'data-03' => ['a' => 'info-03', 'b' => 'extra-03'],
                'data-04' => ['a' => 'info-04', 'b' => 'extra-04'],
                'data-05' => ['a' => 'info-05', 'b' => 'extra-05'],
            ];
            if($data_struct = ($data_group[$_GET['key'] ?? false] ?? false)){
                \header('Content-Type: application/json');
                echo json_encode(['status'=> 'ok', 'data' => $data_struct], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
                exit();
            } else {
                http_response_code(404);
                echo json_encode('Not Found', JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
                exit();
            }
        } break;
    }
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta key="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS Async Data Buffer Experiment</title>
</head>
<body>
    <div class="container">
        
    </div>
    <script>
        xui = {};
        xui.async_cache = {
            async async_get_await(url = '', options = {}){
                // Default options are marked with *
                const response = await fetch(
                    url, 
                    {
                        method: 'GET', // *GET, POST, PUT, DELETE, etc.
                        mode: options.mode || 'same-origin', // no-cors, *cors, same-origin
                        cache: options.cache || 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
                        credentials: options.credintials || 'same-origin', // include, *same-origin, omit
                        redirect: options.redirect || 'follow', // manual, *follow, error
                        referrerPolicy: options.referrerPolicy || 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
                    }
                );
                
                return response.json(); // parses JSON response into native JavaScript objects;
            },
            get(action, key){
                if(key in this){
                    return new Promise((resolve, reject) => {
                        resolve(this[key]);
                    });
                } else {
                    return this.async_get_await(
                        `?--action=${action}&key=${key}`,
                    ).then((r) => {
                        this[key] = r.data;
                        return this[key];
                    });
                }
            },
        };
        
        window.addEventListener('DOMContentLoaded', function(){
            
            var list = [
                'data-01',
                'data-01',
                'data-02',
                'data-01',
                'data-03',
                'data-02',
                'data-04',
                'data-02',
                'data-01',
            ];
            
            list.forEach((key) => {
                console.log({key});
                xui.async_cache.get('data-structure', key).then((data) => {
                    console.log(data);
                });
            });
        });
        
    </script>
</body>
</html>

Case 1 and 2 are taken care of but case 3 is where I am stuck. As you can see the code in the addEventListner executes in one shot before the data is received or event-loop kicks-in. So multiple requests go out for the same data-key.

I am looking for a minimalist approach. If there is a coding feature in JS library without using third party library - that will do as well.

I've also looked into the cache option on the Fetch-API but that is not a solution.

Thanks!

2
  • Cache the promise, not the data. Commented Jan 10, 2023 at 8:53
  • @Bergi - Interesting, I didn't look at it that way. I'll have to give it a try - Thanks! Commented Jan 10, 2023 at 22:30

1 Answer 1

1

Thanks to @Bergi I finally figured it out! I rewrote the async_cache and went on simplifying ... Here is what I got (I've added a few test buttons as well):

<?php 
    switch($_GET['--action'] ?? false){
        case 'data-structure':{
            $data_group = [
                'data-01' => ['a' => 'info-01', 'b' => 'extra-01'],
                'data-02' => ['a' => 'info-02', 'b' => 'extra-02'],
                'data-03' => ['a' => 'info-03', 'b' => 'extra-03'],
                'data-04' => ['a' => 'info-04', 'b' => 'extra-04'],
                'data-05' => ['a' => 'info-05', 'b' => 'extra-05'],
            ];
            if($data_struct = ($data_group[$_GET['key'] ?? false] ?? false)){
                \header('Content-Type: application/json');
                echo json_encode(['status'=> 'ok', 'data' => $data_struct], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
                exit();
            } else {
                http_response_code(404);
                echo json_encode('Not Found', JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
                exit();
            }
        } break;
    }
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta key="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS Async Data Cache Example</title>
</head>
<body>
    <div class="container">
        <button type="button" onclick="run()">Run</button> <br/>
        <button type="button" onclick="delete xui.async_cache['data-01']">Delete 01</button> <br/>
        <button type="button" onclick="delete xui.async_cache['data-02']">Delete 02</button> <br/>
        <button type="button" onclick="delete xui.async_cache['data-03']">Delete 03</button> <br/>
        <button type="button" onclick="delete xui.async_cache['data-04']">Delete 04</button> <br/>
        Look at the console for responses and check the network tab as well!
    </div>
    <script>
        xui = {};
        xui.async_cache = {
            get(key){
                return this[key] ?? (this[key] = (fetch(`?--action=data-structure&key=${key}`).then((response) => {
                    return response.json();
                })));
            }
        };

        function run(){
            
            var list = [
                'data-01',
                'data-01',
                'data-02',
                'data-01',
                'data-03',
                'data-02',
                'data-04',
                'data-02',
                'data-01',
            ];
            
            list.forEach((key) => {
                console.log({key});
                xui.async_cache.get(key).then((data) => {
                    console.log(data);
                });
            });
            
        }
        
        window.addEventListener('DOMContentLoaded', function(){
            run();
        });
        
    </script>
</body>
</html>

Looks like we can take advantage of the closure's data storage (or caching) inside the promise. And we don't need await or async, atleast for this rudimentary stage.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.