0

My question is addition to: How to break out of jQuery each Loop

I need to exit from the jquery $.each() but in this case I cannot return FALSE, since the exit condition is firing a function that is going to resolve a defer. So I need to exit the each, but with a promise.

function KillLayer(id) {
    var defer = $.Deferred();
    $.each(vm.get("DeviceData"), function (idx, item) {
        if (item.Type == id) {
            vm.DeviceData.splice(idx, 1); // remove the item from the list
            removeLayer(defer, id)  // delete it from the PouchDB (IDB) database (async)
            return defer.promise();
        }
    });
    defer.resolve(); // layer was not found, so just resolve & return
    return defer.promise();
}
Community
  • 1
  • 1
Dr.YSG
  • 7,171
  • 22
  • 81
  • 139

4 Answers4

1

try like this.

objDP = null;  // To get Deferred’s Promise object.

$.each(vm.get("DeviceData"), function (idx, item) {
    if (item.Type == id) {
        vm.DeviceData.splice(idx, 1); // remove the item from the list
        removeLayer(defer, id)  // delete it from the PouchDB (IDB) database (async)
        objDP = defer.promise();
        return false;
    }
});
akbar ali
  • 427
  • 2
  • 6
  • You should add a null check of objDp in the beginning of the each loop to prevent the following iterations from doing something. – Danny Varod Dec 17 '13 at 17:55
  • but the following iteration will not run because of return false, please correct me if i am wrong. – akbar ali Dec 17 '13 at 17:58
  • The return is in a delegate function which it ran per iteration, it returns from that function, not from the loop. – Danny Varod Dec 17 '13 at 18:05
  • I am hoping for something cleaner. Right now KillLayer(id) is a simple function that returns a promise that I can WHEN or DONE. I don't really want a global objDP variable that I need to use in conjunction. NOTE: right now I get an console error that item.TYPE is undefined, but the code does run to completion. – Dr.YSG Dec 17 '13 at 18:36
1

As you have discovered, you can't return from KillLayer() inside its each() loop because a return statement in that context has a different meaning.

Here's a couple of different approaches :

1: Use a boolean flag to signal whether or not a deletion took place and treat the Deferred accordingly.

function KillLayer(id) {
    var defer = $.Deferred(), deleted = false;
    $.each(vm.get("DeviceData"), function (idx, item) {
        if (item.Type == id) {
            vm.DeviceData.splice(idx, 1); // remove the item from the list
            removeLayer(defer, id);  // delete it from the PouchDB (IDB) database (async)
            deleted = true;
            return false;//break from each() loop
        }
    });
    return deleted ? defer.promise() : defer().resolve().promise();//return either a promise of deletion or a ready-resolved promise.
}

2: (If you have control over removeLayer()) Arrange for removeLayer() to generate and return its own promise, rather than passing in a Deferred. This will allow you to simplify KillLayer() as follows:

function KillLayer(id) {
    var deletionPromise;
    $.each(vm.get("DeviceData"), function (idx, item) {
        if (item.Type == id) {
            vm.DeviceData.splice(idx, 1); // remove the item from the list
            deletionPromise = removeLayer(id);  // delete it from the PouchDB (IDB) database (async), and receive back a promise
            return false;//break from each() loop
        }
    });
    return deletionPromise || $.Deferred().resolve().promise(); //return either a promise of deletion from the DB, or a ready-resolved promise
}

Although there's only one line difference, I would contend that the second appraoch is cleaner. As removeLayer() performs an asynchronous activity, it should really return a promise of its own, and not rely on a Deferred being passed to it.

Beetroot-Beetroot
  • 18,022
  • 3
  • 37
  • 44
  • 1
    I am going with solution #1 (actually, I figured this out last night, and when I tested it, and went to post it, I saw your answer! – Dr.YSG Dec 18 '13 at 17:17
0

This will not break from the each loop, but it will make the following iterations do nothing:

function KillLayer(id) {
    var defer = $.Deferred();
    var result = null;
    $.each(vm.get("DeviceData"), function (idx, item) {
        if (!result && item.Type == id) {
            vm.DeviceData.splice(idx, 1); // remove the item from the list
            removeLayer(defer, id)  // delete it from the PouchDB (IDB) database (async)
            result = defer.promise();
        }
    });
    return result || defer.resolve();
}
Danny Varod
  • 17,324
  • 5
  • 69
  • 111
  • I am seeing console log errors from Chrome saying that item.Type is undefined. I think this solution would have the same problem. (yea, not fatal, but I prefer not to have errors). – Dr.YSG Dec 17 '13 at 18:33
  • I wondered how comes the iterator delegate has two parameters and not one. I am pretty sure each only passes current item from iteration to delegate. – Danny Varod Dec 17 '13 at 18:39
0

I came up with Beetroots answer #1 independently. Firstly I realize that looping through an $each() and removing items from javascript array that your are looping is a great way for errors. I really should do something like this:

$.each(clone(vm.get("DeviceData")), function (idx, item)` {

where the clone function is defined as:

function clone(obj) {
    return jQuery.extend({}, obj);
}

function deepClone(obj) {
    return jQuery.extend(true, {}, obj);
}

But in my case, I don't need that extra protection, so I just updated my solution as follows:

function KillLayer(id) {
    var defer = $.Deferred();
    var found = false;
    $.each(vm.get("DeviceData"), function (idx, item) {
        if (item.Type == id) {
            vm.DeviceData.splice(idx, 1);
            Compare();
            dbManifest.LayerInfo = vm.DeviceData.toJSON();
            removeLayer(defer, id)
            found = true;
            return false;
        }
    });
    if (!found) defer.resolve(); // layer was not found, so just resolve & return
    return defer.promise();
}
Dr.YSG
  • 7,171
  • 22
  • 81
  • 139