1

I have some code on functions.php that I call via ajax on frontend, I'm using admin-ajax which takes about 2 to 2.5 seconds to load since I have many plugins and that ajax handler need to load all wp core and plugins data, I was wondering how to write a custom ajax handler that only load what is required only to run my snippet below, the brief of my code is that it get woocommerce custom fields via get meta data and read HTTP header for geolocation purpose and implement some variables that I defined to create a button with a link.

function simple_amz_link_ajax() {   
    ?>
    <script>
        jQuery(document).ready(function(){

            jQuery.ajax({
                url: "<?php echo admin_url('admin-ajax.php?action=get_amz_btn'); ?>",
                type: 'POST',
                data: {
                        action: 'get_simple_amz_button',
                        postId: <?php echo get_post()->ID; ?>
                },
                dataType: 'html',
                success: function(response) {
                jQuery("#buy_amz_btn_wrap").html(response);

                }

            }); 
        });
    </script> 
    <!-- end Ajax call to get_simple_amz_button -->

    <div id="buy_amz_btn_wrap">

    <div class="spinner">

      <div class="bounce1"></div>
      <div class="bounce2"></div>
      <div class="bounce3"></div>
    </div>

        </div>
    <?php
}



add_action('wp_ajax_get_simple_amz_button', 'simple_amz_button');
add_action('wp_ajax_nopriv_get_simple_amz_button', 'simple_amz_button');

function simple_amz_button() {  
// Variables Declaration
    $postId = filter_input( INPUT_POST, 'postId', FILTER_SANITIZE_NUMBER_INT );
    $de_asin = get_post_meta( $postId, "wccaf_de_asin", true );

    $country_code = $_SERVER ["HTTP_CF_IPCOUNTRY"];
    $not_avilable_country = '<div id="amz_not_avilable" class="amz_not_avilable">This product is not avilable in your country yet</div>';

    // Get Amazon Button Title  
    if (ICL_LANGUAGE_CODE == "de") {
        $amz_btn_title = 'Kaufen auf Amazon'; 
        $not_avilable_country = '<div id="amz_not_avilable" class="amz_not_avilable">Dieses Produkt ist in Ihrem Land noch nicht verfügbar</div>';
    }
    if (ICL_LANGUAGE_CODE == "en")  {
        $amz_btn_title = 'Buy on Amazon'; 
        $not_avilable_country = '<div id="amz_not_avilable" class="amz_not_avilable">This product is not avilable in your country yet</div>';
    }
        //////////////////////////////////////////////
    // Geolocation Condition
    if ($country_code=="DE" or $country_code=="DE" or $country_code=="AT" or $country_code=="CH" or $country_code=="LI" or $country_code=="EG") {
        $associate_id = "bonstato-21";
        $access_key = "HDUHWUIDIUWJDWDWDWD";
        $secret_key = "HDUIWQDUQWUDJUIQJWDJWQD";
        $amazon_domain = "amazon.de";
        $asin = $de_asin;
    }



    /**********************************************************************************/

    // Get price from amazon

    $amazon = new AmazonAPI($associate_id , $access_key, $secret_key , $amazon_domain);
    $item = $amazon->item_lookup($asin)->get_item_data();
    if ($item->price != "0" && $item->price != null ) {
    ?><div class="amz_price_wrap_wrap" >Price: <?php echo $item->price; ?></div><?php
    }

    global $post;
    $product = wc_get_product( $postId );
    $type = $product->get_type();
    if( $type == 'simple' && $item->price != "0"  && $item->price != null ){    
        if( wp_is_mobile() ) {
            // Amazon Link For Mobile       
            ?>
            <div class="buy_amz_btn_wrap" >     
            <button type="button" id="buy_amz_btn" class="buy_amz_btn" onclick="window.location='https://<?php echo $amazon_domain ?>/dp/<?php echo $asin ?>/?tag=<?php echo $associate_id ?>';"><i class="fa fa-amazon fa-amz"></i><?php echo $amz_btn_title ?></button>                        
            </div>
            <?php
        }

        else {
            // Amazon Link For PC
            ?>
             <div class="buy_amz_btn_wrap" >    
            <button type="button" id="buy_amz_btn" class="buy_amz_btn" onclick="window.location='https://<?php echo $amazon_domain ?>/gp/aws/cart/add.html?AssociateTag=<?php echo $associate_id ?>&ASIN.1=<?php echo $asin ?>&Quantity.1=1';"><i class="fa fa-amazon fa-amz"></i><?php echo $amz_btn_title ?></button>                          
            </div>
            <?php 
        }
    }

    else if( $type == 'simple' && $item->price == "0"){  
        echo $not_avilable_country;
    }


    if(is_null($item->price)){   
        echo $not_avilable_country;
    }





die(); 

} 
  • I would note that until all the stuff has been loaded, your code won't run by which time it's too late. You might want to consider not having as many plugins do so many things that it takes 2 seconds? It's difficult to tell without a full copy of your codebase, e.g. you might be doing DB writes, or expensive queries on every request that don't always need to happen, etc – Tom J Nowell Sep 28 '18 at 0:59
  • Also, have you considered using a REST API endpoint instead of Admin AJAX? – Tom J Nowell Sep 2 '19 at 12:25
2

Here's the answer you're searching for:

Ajax takes 10x as long as it should/could

Unfortunately, the solution there involves very detailed knowledge of what parts to load / what not to load and why stuff would break. Reading through github issues / comments, it seems this doesn't work with HTTPS.

Otherwise, you really can't speed it up, I've researched this extensively.

But may I suggest the REST API? The RA is amazing for just pulling data that is public. Think products, posts and any other information that's just informational.

This has sped up my scripts by a ton when I decided to switch over.

Give it a try, see if it fits your use case. From the looks of it, it does. You're not disclosing anything, so a rest endpoint with read-only functions would suit you.

Here are my 1:1 tests of Rest API vs. AJAX in my case.

I'm simply retrieving some data that I'll then use to display -

WP AJAX:

function get_block_help_data(block_identifier) {

    return jQuery.ajax({
        url: block_help_data.ajax_url,
        type: 'POST',
        data: {
            action: 'parse_block_help_request',
            security: block_help_data.ajax_nonce,
            block_identifier: block_identifier
        },
    });
}

Implemented in the back-end:

add_action( 'wp_ajax_parse_block_help_request', array( $this, 'parseBlockHelpRequest' ) );

public function parseBlockHelpRequest()
{
    check_ajax_referer( 'block_help_nonce', 'security' );
    $block_identifier = sanitize_text_field( $_POST['block_identifier'] );

    //Check if the data is what we need.
    if( ( empty( $block_identifier ) || is_wp_error( $block_identifier ) ) ) {
        wp_send_json( 'Invalid data.', 500 );
        return;
    }

    wp_send_json( DataPool::getBlockHelpItem( $block_identifier ) );
}

REST:

function get_block_help_data(block_identifier) {

    return jQuery.ajax({
        url: block_help_data.rest_link + block_identifier,
        type: 'GET',
        dataType: 'JSON'
    });
}

Implemented in the back-end:

class BlockHelpREST extends \WP_REST_Controller
{
    /**
     * Holds the namespace for the REST endpoint.
     *
     * @var string
     */
    protected $block_help_namespace = 'block_help/v';

    /**
     * Holds the version for the REST endpoint.
     *
     * @var string
     */
    protected $block_help_version = '1';

    public function __construct()
    {
        $this->hookRestRouteToServer();
    }

    /**
     * Registers the main routes for the Block Help REST API.
     */
    public function registerRoutes() {
        $namespace = $this->block_help_namespace . $this->block_help_version;
        $base = 'block_identification';

        register_rest_route(
            $namespace, '/' . $base,
            array(
                array(
                    'methods' => \WP_REST_Server::READABLE,
                    'callback' => array( new DataPool, 'getBlockHelpItemByREST' ),
                    //'permission_callback' => array( new Privileges, 'canLoadSprout' )
                )
            )
        );
    }

    public function hookRestRouteToServer(){
        add_action( 'rest_api_init', array( $this, 'registerRoutes' ) );
    }
}

Over 20-30 runs (I know, not enough, but I can clearly see with the eye that WP AJAX is a tad bit slower):

The REST call took: MOSTLY 150ms with one high of 215.

The API call always, without exception, took at least ~400-500ms.

Ignoring that one high, this is incredible gains, but do keep in mind, the sample is small. I'll keep testing and update because it, indeed, seems too good to be true.

  • buzzwords by themselves do not solve problems. REST API is not much faster than the AJAX one, especially if you already have a good server configuration. – Mark Kaplun Sep 28 '18 at 4:36
  • @MarkKaplun In my case, the REST API is instant, where as AJAX would be ~2seconds. – coolpasta Sep 28 '18 at 4:40
  • seriously? on most sites TTFB is about 1s no matter what you do. If you get anything "instant" it means that the code producing it didn't comply with WP development best standards (yes I know it sounds weird, but you do not use wordpress for its REST handling performance) – Mark Kaplun Sep 28 '18 at 4:48
  • the tested difference between REST and "AJAX" is 10% – Mark Kaplun Sep 28 '18 at 4:49
  • @MarkKaplun If time allows (I promise I won't forget), I'll run the test just to prove it, I, myself don't like this kind of "let's just say it's faster and not provide nubmers". My pain point for selecting REST vs. AJAX was exactly this. – coolpasta Sep 28 '18 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.