0

I'm using AngularJs and the module ngSweetAlert http://oitozero.github.io/ngSweetAlert/#/home, i have to wait for the execution of instructions in the confirmation button function in a for loop :

for (var i = 0; i < myArray.length; i++) { 
    SweetAlert.swal({
        title: "Are you sure?",
        text: "Your will not be able to recover this imaginary file!",
        type: "warning",
        showCancelButton: true,
        confirmButtonColor: "#DD6B55",
        confirmButtonText: "Yes, delete it!",
        cancelButtonText: "No, cancel plx!",
        closeOnConfirm: false,
        closeOnCancel: false
    }, function(isConfirm) { 
        if (isConfirm) {
            // Instructions using myArray
        } else {

        }
    });
}
4castle
  • 32,613
  • 11
  • 69
  • 106
BruceWayne
  • 53
  • 9
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – 4castle Jun 16 '16 at 22:19
  • @4castle This has nothing to do with JS closures in loops. Its about making the invocation of the callback block the processing of the loop. – Asad Saeeduddin Jun 16 '16 at 22:20
  • Have a go at my solution, but really quick... are you trying to create array.length number of confirmation boxes appear at once? because i'm pretty sure thats going to be the result of what you have. each time you call the sweetalert.swal function it creates a confirmation box, so you're either going to get potentially a lot of confirmation boxes or you're going to get one confirmation box thats going to be overwritten a bunch. you might want to move the sweet box function invocation outsjde the loop – Andrew Luhring Jun 16 '16 at 22:23
  • 1
    @AsadSaeeduddin You're right, but they will need to use closures to do this properly, otherwise, `i` is going to be `myArray.length` for every single one of the callbacks. – 4castle Jun 16 '16 at 22:26
  • The fact that they are going to incidentally need closures does not make this a duplicate of the linked question. This is not what "close as duplicate" is for. – Asad Saeeduddin Jun 16 '16 at 22:27
  • also 4castle is right the way you have it written you're only going to get the last value in the array unless you use a closure. thats what i meant when i said youre going to have one sweetbox thats overwritten a bunch – Andrew Luhring Jun 16 '16 at 22:27
  • @AndrewLuhring I'm not sure what you're referring to when you say "you're only going to get the last value in the array unless you use a closure". The OP isn't using `i` anywhere within the loop body. What do they need to close over? – Asad Saeeduddin Jun 16 '16 at 22:29
  • @AsadSaeeduddin Considering they need to use the array in their callback, `i` is going to be necessary in order for the program to do something meaningful with the array. – 4castle Jun 16 '16 at 22:31
  • @asad in his if statement his comment says "instructions using myArray". if he's using myArray[i] anything, his execution context is always going to be the last item in myArray. – Andrew Luhring Jun 16 '16 at 22:34
  • @4castle We don't know what the OP is trying to do in the callback. Also, if they wanted to use a per-iteration value of `i` they would want to *avoid* using a closure, and instead pass `i` as an argument. – Asad Saeeduddin Jun 16 '16 at 22:34
  • why would he be looping over the array if he didnt want to do something with each value? if he passes i as an argument its only going to be the last item because he's not maintaining the state of i... right? – Andrew Luhring Jun 16 '16 at 22:45
  • @AndrewLuhring No, only if you pass `i` as an argument will it not be the last value. If you *close* over `i`, the value inside the callback will be the final value of `i`, this is what happens if you just naively refer to `i` inside the body of the callback. Instead, what you can do is wrap the callback in an IIFE that *accepts* `i` as an argument. Since it's accepted as an argument, it isn't updated as the loop progresses. – Asad Saeeduddin Jun 16 '16 at 22:48

3 Answers3

1

EDIT: Original polling example was incorrect, as pointed out by Andrew Luhring

Here's how you could use the asynchronous callback style to schedule each alert popup after the former finishes:

function showAlertNTimes(n) {
    if (n > 0) {
        SweetAlert.swal({...}, function() {
            // Do confirmation stuff 
            showAlertNTimes(n - 1); 
        });
    }

}
showAlertNTimes(myArray.length);
Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
  • Not positive, but i'm pretty sure calling a while loop without providing an exit is going to crash the page. i know that's what your "done" variable is supposed to do. if you want to poll something like that you could probably use setInterval that checks done each time and then use clearInterval once done is true, but still i agree with you thays not a good way to solve his problem. – Andrew Luhring Jun 16 '16 at 22:41
  • Yes, you're right. The while won't work with single threaded JS. Unfortunately you can't implement a blocking poll loop with setInterval either, for the same reason you can't directly block on the result of the the alert, they're asynchronous APIs that accept a callback. – Asad Saeeduddin Jun 16 '16 at 22:44
  • @AndrewLuhring Thanks for the correction, I've updated the answer. – Asad Saeeduddin Jun 16 '16 at 22:54
  • There also seems to be a [`swal.queue`](https://limonte.github.io/sweetalert2/#chaining-modals) function for chaining modals. But I'm not sure if the OP is using a new enough version. Also, I couldn't find a way to give callbacks to the queued modals. – 4castle Jun 16 '16 at 22:58
  • @AsadSaeeduddin Thank you so much !! – BruceWayne Jun 17 '16 at 01:06
0

Use a promise:

var deferred = $q.defer();
q.resolve(isConfirm); // or whatever ur data is
return deferred.promise;

then use "then" to specify what should be done with the result of the returned promise;

yourFn().then(function(isConfirm){
  //do stuff
});
Andrew Luhring
  • 1,762
  • 1
  • 16
  • 37
0

beware, untested

Not familiar with swal, but after a quick look into it's source code it doesn't seem to provide promises, so I wrote a little wrapper (based on the src, on the linked demo-page):

//since swal doesn't implement promises on it's own, here a wrapper that returns one
function $swal(title, message, type){
    return typeof message === "function"?  $swal(title).then(message):
        typeof title !== "object"? $swal({ title:title, message:message, type:type }):
        config => $q(function(resolve){ window.swal(config, resolve) });
}

this should behave just like the regular swap-function just that it returns a promise. needs on $q from angular and an initialized SweetAlert

now asyncromity get's easy:

myArray.reduce(function(prev, value, index, arr){
    //wait till the previous promise has been resolved, 
    //then resolve the this one
    return prev.then(function(){
    //return prev.catch(/* error handling for prev */).then(function(){

        //utilize the wrapped function. 
        return $swal({
            title: "Are you sure?",
            text: "Your will not be able to recover this imaginary file!",
            type: "warning",
            showCancelButton: true,
            confirmButtonColor: "#DD6B55",
            confirmButtonText: "Yes, delete it!",
            cancelButtonText: "No, cancel plx!",
            closeOnConfirm: false,
            closeOnCancel: false
        //}).then(function(isConfirm){
        }, function(isConfirm){  //this is just an alias for the previous line
            console.log(index, isConfirm, value);
        });
    });
}, $q.resolve(true));

I pass in a resolved Promise, so I don't have to deal with wether it's the first or a dependant call.

reduce also wrapps the current index and value for each call.

One thing that's not handled yet, is if your code throws an error.

Edit: As 4castle pointed out in a comment, SweetAlert2 seems to implement promises.
Well, then you simply don't need the $swal-wrapper, and use the regular swal-function with reduce.

Thomas
  • 3,513
  • 1
  • 13
  • 10