Search code examples
javascriptnode.jsarray-pushnode-deasync

Node js async convert to sync


I need to push data to array synchronously. First API request get image key base one that need to get image data within loop.

 var deasync = require('deasync');


        router.get('/a', function(req, res) {
                var username="user";
                var passw ="pass";
                var op = [];
                var args = {
                    headers: {
                    'Authorization': 'Basic ' + new Buffer(username + ':' + passw).toString('base64')
                }   
            };
         //this is first api request
        client.get(global.apiUrl+"V1/ProductItem", args,
        function (data, response) {
                //this is second api request
            data.forEach(function(img) {client.get(global.apiUrl+"V1/ImagePreview/"+img.AvatarKey,args,
                    function (data2, response){
                               img['data']=data2.Image;
                               deasync(pushData(img));
                          });
                    });     
                });

        function pushData(img){
            op.push(img);//array push 
        }
        res.render('test1', { "out":JSON.stringify(op) });
        });

Solution

  • As much as I think deasync is a poor choice of solving your particular issue, the key to using it, is to "deasync" asynchronous functions. As Array.push is synchronous, deasync'ing Array.push makes no sense

    having read the documentation for deasync, it's fairly simple to use

    var deasync = require('deasync');
    // create a sync client.get
    function syncClientGet(client, url, args) {
        var inflight = true;
        var ret;
        client.get(url, args, function(data, response) {
            // as your original code ignores response, ignore it here as well
            ret = data;
            inflight = false;
        });
        deasync.loopWhile(() => inflight);
        return ret;
    }
    
    router.get('/a', function(req, res) {
        var username = "user";
        var passw = "pass";
        var op = [];
        var args = {
            headers: {
                'Authorization': 'Basic ' + new Buffer(username + ':' + passw).toString('base64')
            }
        };
    
        let data = syncClientGet(client, global.apiUrl + "V1/ProductItem", args);
        data.forEach(function(img) {
            let data2 = syncClientGet(client, global.apiUrl + "V1/ImagePreview/" + img.AvatarKey, args);
            img['data'] = data2.Image;
            op.push(img);
        });
        res.render('test1', {
            "out": JSON.stringify(op)
        });
    });
    

    However, embracing asynchronicity, the code you posted could be easily written as

    router.get('/a', function (req, res) {
        var username = "user";
        var passw = "pass";
        var op = [];
        var args = {
            headers: {
                'Authorization': 'Basic ' + new Buffer(username + ':' + passw).toString('base64')
            }
        };
        client.get(global.apiUrl + "V1/ProductItem", args, function (data, response) {
            data.forEach(function (img) {
                client.get(global.apiUrl + "V1/ImagePreview/" + img.AvatarKey, args, function (data2, response) {
                    img['data'] = data2.Image;
                    op.push(img);
                    if (img.length == data.length) {
                        res.render('test1', {
                            "out": JSON.stringify(op)
                        });
                    }
                });
            });
        });
    });
    

    or, using Promises

    router.get('/a', function (req, res) {
        var username = "user";
        var passw = "pass";
        var args = {
            headers: {
                'Authorization': 'Basic ' + new Buffer(username + ':' + passw).toString('base64')
            }
        };
        // create a Promisified client get
        var clientGetPromise = function clientGetPromise(client, url, args) {
            return new Promise(function (resolve, reject) {
                return client.get(url, args, function (data, response) {
                    return resolve(data);
                });
            });
        };
    
        clientGetPromise(client, global.apiUrl + "V1/ProductItem", args).then(function (data) {
            return Promise.all(data.map(function (img) {
                return clientGetPromise(client, global.apiUrl + "V1/ImagePreview/" + img.AvatarKey, args).then(function (data2) {
                    img['data'] = data2.Image;
                    return img;
                });
            }));
        }).then(function (op) { // op is an Array of img because that's how Promise.all rolls
            return res.render('test1', { "out": JSON.stringify(op) });
        });
    });