0

I have a simple for loop, which basically checks if the images are stored in the file system, if not, then download it and render the UI:

for (var t = 0; t < toJSON.length; t++) {
    if (t < 3) {
        var view = Titanium.UI.createImageView({
            width: 320,
            height: 310,
            //top: 10 
        });
        image_url = toJSON[t];
        //start
        if (utilities.CheckIfImageExist(utilities.ExtractImageName(image_url))) {
            var parent = Titanium.Filesystem.getApplicationDataDirectory();
            var picture = Titanium.Filesystem.getFile(parent, 'pictures');
            var picturePath = parent + 'pictures/';
            Ti.API.info('picturePath: ' + picturePath);
            var f = Titanium.Filesystem.getFile(picturePath, utilities.ExtractImageName(image_url));
            var blob = f.read();
            // here is saved blog file
            console.log('Image already downloaded');
            var width = blob.width;
            var height = blob.height;
            //crop  so it fits in image view
            if (width > height) {
                view.image = ImageFactory.imageAsCropped(blob, {
                    width: height,
                    height: height,
                    x: 60,
                    y: 0
                });
            } else {
                view.image = ImageFactory.imageAsCropped(blob, {
                    width: (width - 1),
                    height: (width - 1),
                    x: 60,
                    y: 0
                });
            }
        } else {
            //do new loop - async causing problems
            alert('not downloaded');
            // if image is not downloaded we will download it here
            utilities.APIGetRequestImage(image_url, function (e) {
                alert('begin downloaded');
                var status = this.status;
                if (status == 200) {
                    Ti.API.info(this.responseData);
                    //save to directory
                    utilities.SaveImageToDirectory(this.responseData, image_url);
                    //create view
                    var view = Titanium.UI.createImageView({
                        width: 320,
                        height: 310,
                        //top: 10
                    });
                    var width = this.responseData.width;
                    var height = this.responseData.height;
                    //crop  so it fits in image view
                    if (width > height) {
                        var view = Titanium.UI.createImageView({
                            width: 320,
                            height: 310,
                            //top: 10
                        });
                        view.image = ImageFactory.imageAsCropped(this.responseData, {
                            width: height,
                            height: height,
                            x: 60,
                            y: 0
                        });
                        //  $.scrollableView.addView(view);
                        viewArr.push(view);
                    } else {
                        view.image = ImageFactory.imageAsCropped(this.responseData, {
                            width: (width - 1),
                            height: (width - 1),
                            x: 60,
                            y: 0
                        });
                        viewArr.push(view);
                        //when t = 3, all views are put inside array, set image view
                        //if(t==3){
                        //}
                    }
                }
            }, function (err) {
                alert('error downloading image');
            });
        }
    }
}

The code, where it says "begin download" only executes after the for loop executes the first half of the IF statement (where it says "not downloaded"), by that t=3.

The for loop then executes the else statement, the trouble I have is that I need it to do it in a synchronized manner, as I am reliant on the t value to know which image to download and place in the view.

    utilities.APIGetRequestImage(image_url, function(e) {

is a callback method which gets the file from the server and downloads it.

How can I get both methods to run concurrently?

wachme
  • 2,327
  • 20
  • 18
bobo2000
  • 1,779
  • 7
  • 31
  • 54
  • What's your question? – Traveling Tech Guy May 04 '14 at 17:27
  • the image_url, if it fails the first check of the if statement, it should be passed into the last part of the if statement for each iteration. I suspect due to the last part being an async function, it is not iterating properly – bobo2000 May 04 '14 at 17:30
  • http://geekabyte.blogspot.co.uk/2013/04/callback-functions-in-loops-in.html this is another example - callblacks in loop – bobo2000 May 04 '14 at 17:41
  • That shouldn't read "begin download", but rather "ended download, begin saving"? – Bergi May 04 '14 at 18:03

2 Answers2

1

Check it out:

for (var t = 0; t < toJSON.length; t++) {
    if (t < 3) {
        var view = Titanium.UI.createImageView({
            width: 320,
            height: 310,
            //top: 10 
        });
        image_url = toJSON[t];
        //start
        if (utilities.CheckIfImageExist(utilities.ExtractImageName(image_url))) {
            var parent = Titanium.Filesystem.getApplicationDataDirectory();
            var picture = Titanium.Filesystem.getFile(parent, 'pictures');
            var picturePath = parent + 'pictures/';
            Ti.API.info('picturePath: ' + picturePath);
            var f = Titanium.Filesystem.getFile(picturePath, utilities.ExtractImageName(image_url));
            var blob = f.read();
            // here is saved blog file
            console.log('Image already downloaded');
            var width = blob.width;
            var height = blob.height;
            //crop  so it fits in image view
            if (width > height) {
                view.image = ImageFactory.imageAsCropped(blob, {
                    width: height,
                    height: height,
                    x: 60,
                    y: 0
                });
            } else {
                view.image = ImageFactory.imageAsCropped(blob, {
                    width: (width - 1),
                    height: (width - 1),
                    x: 60,
                    y: 0
                });
            }
        } else {
            //do new loop - async causing problems
            alert('not downloaded');
            // if image is not downloaded we will download it here
            utilities.APIGetRequestImage(image_url, (function (t, image_url) {
                return function (e) {                               // <----- wrap callback function
                    alert('begin downloaded');
                    var status = this.status;
                    if (status == 200) {
                        Ti.API.info(this.responseData);
                        //save to directory
                        utilities.SaveImageToDirectory(this.responseData, image_url);
                        //create view
                        var view = Titanium.UI.createImageView({
                            width: 320,
                            height: 310,
                            //top: 10
                        });
                        var width = this.responseData.width;
                        var height = this.responseData.height;
                        //crop  so it fits in image view
                        if (width > height) {
                            var view = Titanium.UI.createImageView({
                                width: 320,
                                height: 310,
                                //top: 10
                            });
                            view.image = ImageFactory.imageAsCropped(this.responseData, {
                                width: height,
                                height: height,
                                x: 60,
                                y: 0
                            });
                            //  $.scrollableView.addView(view);
                            viewArr.push(view);
                        } else {
                            view.image = ImageFactory.imageAsCropped(this.responseData, {
                                width: (width - 1),
                                height: (width - 1),
                                x: 60,
                                y: 0
                            });
                            viewArr.push(view);
                            //when t = 3, all views are put inside array, set image view
                            //if(t==3){
                            //}
                        }
                    }
                };
            })(t, image_url), function (err) {
                alert('error downloading image');
            });
        }
    }
}

By wrapping utilities.APIGetRequestImage callback, t and image_url are passed correctly.

wachme
  • 2,327
  • 20
  • 18
  • Watchme, that did the trick. What exactly as happening here? Why did that work? BIG THANKS – bobo2000 May 04 '14 at 18:48
  • Wrapper function (closure) is called synchronously. The callback function uses closure's inner scope that doesn't change when outer scope does. Read more about closures: http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – wachme May 04 '14 at 19:15
  • by the way I have added an alert to check the t value within the async function, and it seems to be going in this order 1,0,2 , instead of 0,1,2 - not sure why that is happening – bobo2000 May 04 '14 at 19:18
  • Test it multiple times, results probably will be different. It's because `utilities.APIGetRequestImage` execution doesn't always take same time. For some reason second call ended before the first one. – wachme May 04 '14 at 19:23
  • is there anyway I can programmacially prevent this from happening? OR would it be better too hardcode say 3 divs (3 image views) and just ensure the right image goes into the right view according to the t value? – bobo2000 May 04 '14 at 19:45
  • Definitely the second option. Since there are asynchronous calls, you should explicitly declare the order of images. – wachme May 04 '14 at 19:52
  • I see, even if i can get away with it now, the trouble is , if I had say a 100+ images, it probably wouldn't be the best way to do things – bobo2000 May 04 '14 at 19:59
  • Another approach would be to create one div at a time with order info `
    `. View iterates through created divs and appends new one right before first one that has `data-order` attribute greater than `t`. Hope you understand ;)
    – wachme May 04 '14 at 20:21
  • Thanks a lot wachme, by the way you wrote earlier that the async method execution doesnt always take the same time - since we have used closures, shouldn't it iterate by t in order anyway as opposed to ranodmly. By the way my approach did work in the end. May try yours! :) – bobo2000 May 04 '14 at 20:29
  • Closures ensure scope invariability - they store copies of arguments state, but they don't synchronize code. The loop always iterates by `t` and calls `utilities.APIGetRequestImage` in strict order, but nothing can ensure that downloading will end in the same order. – wachme May 04 '14 at 20:40
0

When saying that you "need it to do it in a synchronized manner", then you don't actually. You say that you want to "get both methods to run concurrently", which requires asynchrony.

Just swap the check if(t==3) (which is always 3, and even if you did put a closure around it like @wachme suggested then it would just be 3 for the download that started last - not the one that ended last) for a check of the number of items you've received: if(viewArr.length==3). It will be 3 when all three callbacks are executed, and you can continue executing your tasks.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375