Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use promises, or complete an ajax request before the function finishes?

I have the following function to check a users session to see if they're staff or not. Now, I know there are better ways to do this, but I'm trying to make a simple application that's tied with a forum software.

function isStaff(callback) {
    $.ajax({
        url: url
    }).done(function(data) {
        var session = $.parseJSON(data);
        if (session.is_staff === 1) {
            callback(true);
        } else {
            callback(false);
        }

    });
}

Let's say I'm using this function in, like so, when compiling a "post" (Handlebars).

function compilePost(post) {
    var source = $('#feed-item-template').html();
    var template = Handlebars.compile(source);
    var context = {
        id: post.id,
        content: post.text,
        author: post.author,
        date: $.timeago(post.date),
        staff: function() {
            isStaff(function(response) {
                return response;
            });
        }
    }
    var html= template(context);
    return html;
}

Problem here, is that the request to check if a user is staff doesn't complete the request until after the function is ran.

I know with Promises is an alternative to async: false, where request is made and the response comes back before the function finishes.

But I have no idea how I can convert this into a promise. I've tried to learn it but I'm stuck at the concept. Can someone explain this to me? Thanks.

like image 806
muser Avatar asked Jun 10 '16 07:06

muser


People also ask

How do I make jQuery wait for an AJAX call to finish before it returns?

click( function(){ $. ajax({ url: $(this). attr('href'), type: 'GET', cache: false, timeout: 30000, error: function(){ return true; }, success: function(msg){ if (parseFloat(msg)){ return false; } else { return true; } } }); }); jquery.

Does AJAX use promise?

ajax returns a promise object, so that we don't have to pass a callback function.

What is promise in AJAX call?

promise() will attach the methods onto it and then return this object rather than create a new one. This can be useful to attach the Promise behavior to an object that already exists. Note: The returned Promise is linked to a Deferred object stored on the . data() for an element. Since the .

Does AJAX call return promise?

ajax returns, which is a jqXHR object that conforms to the promise interface. If there is a failure, the outer fail function is invoked. The outer fail function is also invoked if the processData function fails. When both the getData and processData functions are successful, the outer done method is invoked.


2 Answers

First, let's simplify the compilePost function. This function should know how to compile a post in a synchronous manner. Let's change the isStaff fetching to a simple argument.

function compilePost(post, isStaff) {
    var source = $('#feed-item-template').html();
    var template = Handlebars.compile(source);
    var context = {
        id: post.id,
        content: post.text,
        author: post.author,
        date: $.timeago(post.date),
        staff: isStaff
    }

    var html= template(context);
    return html;
}

Now, let's create a new method, with a single purpose - checking if a user is member of the staff:

function checkForStaffMemebership() {
    return new Promise(function (resolve, reject) {
        $.ajax({
            url: url,
            success: function (data) {
                var session = $.parseJSON(data);
                if (session.is_staff === 1) {
                    resolve(true);
                } else {
                    resolve(false);
                }
            }
        });
    });
}

This function wraps your original ajax call to the server with a promise, whenever the $.ajax call gets a response from the server, the promise will resolve with the answer whether the user is a staff member or not.

Now, we can write another function to orchestrate the process:

function compilePostAsync(post) {
    return checkForStaffMemebership()
        .then(function (isStaff) {
            return compilePost(post, isStaff);
        });
}

compilePostAsync finds out whether the user is a staff member or not. Then, it's compiling the post.

Please notice that compilePostAsync returns a promise, and thus if you used to have something like:

element.innerHTML = compilePost(post);

Now, you should change it to something like:

compilePostAsync(post).then(function (compiledPost) {
    element.innerHTML = compiledPost;
});

Some notes:

  1. This is only an example, it surely misses some things (proper error handling for example)
  2. The isStaff and checkForStaffMemebership (original and new) do not get any argument, I guess you'd figure out how to pass the userId or any other data you might need
  3. Read about promises, it's a useful tool to have, there is a lot of data about it on the web, for example: MDN.
like image 112
Gilad Artzi Avatar answered Nov 06 '22 21:11

Gilad Artzi


As per the documentation you dont need to wrap the ajax with a promise which already implements promise. Instead chain the response as explained below.

The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (see Deferred object for more information)

You can do something like below by chaining the response:

function isStaff(url, post) {
    return $.ajax({
        url: url,
        dataType:"json"
    }).then(function(resp){
        //resp = $.parseJSON(resp); /*You dont require this if you have respose as JSON object. Just Specify it in 'dataType'*/
        var source = $('#feed-item-template').html();
        var template = Handlebars.compile(source);
        var context = {
            id: post.id,
            content: post.text,
            author: post.author,
            date: $.timeago(post.date),
            staff: resp.is_staff === 1 ? true : false
        };

        return template(context);
    });
}

isStaff(url, post).done(function(template){
    /*Your compiled template code is available here*/
}).fail(function(jqXHR, textStatus, errorThrown){
   console.log("Error:"+textStatus);
});

Note: Be sure to implement error callbacks also. Because you may never know what went wrong :)


Simple explanation about promise with $.defer:

For understanding i have created the Fiddle similar to your requirement.

Explanation:

Basically Promise is been introduced to attain synchronous execution of asynchronous JS code.

What do you mean by Async or Asynchronous code?

The code that is executed may return a value at any given point of time which is not immediate. Famous example to support this statement would be jquery ajax.

Why is it required?

Promise implementations helps a developer to implement a synchronous code block which depends on asynchronous code block for response,. like in ajax call when i make a request to server asking for a data string, i need to wait till the server responds back to me with a response data string which my synchronous code uses it to manipulate it , do some logic and update the UI.

Follow this link where the author has explained with detailed examples.

PS: Jquery $.defer implements or wraps promise in quite a different way. Both are used for the same purpose.

like image 21
NiRUS Avatar answered Nov 06 '22 23:11

NiRUS