skip to Main Content

I am not a JS guy (can write some basic stuff), I am working on integrating our Payment API with the front end, as part of the process, we want to trigger a timeout in case my back-end Payment API is not responding within a given timeframe. Here is the JS code which interacting with the Payment API.

performAuthorizePaymentRequest: function (payment) {
        return new Promise(function (resolve, reject) {
            $.ajax({
                type: 'POST',
                url: encodedContextPath + '/checkout/authorise_order',
                data: JSON.stringify(payment),
                dataType: 'json',
                contentType: 'application/json',
                success: resolve,
                error: reject,
                always: function () {
                     pageSpinner.end() 
                },
                timeout: 30000 /* call this.session.completePayment() within 30 seconds or the payment is cancelled */
            });
       });
    }

This seems to be working fine, however I have seen the following issue.

  1. In case m backed API throws any error, the code is still waiting for the timeout.
  2. Even for success cases, the js is waiting for timeout before showing success message.

Is there a way to handle this? Let me know in case I am doing something wrong here.

Note: This code will only execute of Safari, for other browser this is not available

7

Answers


  1. I would avoid a timeout in the ajax call, but would go to a with a set_time_limit(30) in the php (if your /checkout/authorise_order is a php…)

    if the answer arrive before, all fine (won’t wait untill 30sec) it’s a time limit…

    For javascript part I use my own :

    function ajaxLive ( divID ) {
        var pcache = (Math.floor(Math.random() * 100000000) + 1);
        var params = "divID="+encodeURIComponent(divID);
        var xhr = new XMLHttpRequest(); 
        xhr.open("POST", "path_to_script.php?pcache="+pcache, true);
        xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
        xhr.onprogress = function(e) { if (xhr.readyState == 4) { $("#"+divID).html(e.currentTarget.responseText) ; } }
        xhr.send(params);
    }
    

    in this function pcache if crucial to avoid caching because the purpose of api calls is to have a different answer each time…

    in the params you can add extra stuff too and it will pass it to php

    If you use php curl or shell curl, there is also max time mechanisms

    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); // for the connection (not the answer)
    curl_setopt($ch, CURLOPT_TIMEOUT, 30); //timeout in seconds
    
    Login or Signup to reply.
    • I will suggest you something I don’t know if you will like it or will works fine for you or not but you can use setInterval() method this method will repeat your code automatically for many times you can create a condition if event happend stop this setInterval() function

    Example

        var check = false;
        setInterval(function() {
            if (check === true) {
                return;
            }
            $.ajax({
                type: 'POST',
                url: encodedContextPath + '/checkout/authorise_order',
                data: JSON.stringify(payment),
                dataType: 'json',
                contentType: 'application/json',
                success: function(e) {
                    if (e.status === 200) {
                        check = true;
                    }
                },
                always: function() {
                    pageSpinner.end()
                }
            });
        }, 10);
    
    Login or Signup to reply.
  2. You can abort the timeout like this maybe ? the example is at error, but you can do the same at success

    $.ajax({
    url: "/your_ajax_method/",
    type: "GET",
    dataType: "json",
    timeout: 3000, //Set your timeout value in milliseconds or 0 for unlimited
    success: function(response) { alert(response); },
    error: function(jqXHR, textStatus, errorThrown) {
        if(textStatus!=="timeout")  
              jqXHR.abort("timeout");    
    }
    

    });​

    Login or Signup to reply.
  3. I would recommend sticking to fetch instead of ajax because it timeouts after 300s by default and does fail if the server returns an error.

    You can also abort the request before the default timeout using an abort controller https://developer.mozilla.org/en-US/docs/Web/API/AbortController.

    The same code in fetch would be:

    
    const performAuthorizePaymentRequest = (payment) =>
      fetch(`${encodedContextPath}/checkout/authorise_order`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(payment), 
      }).then(r=>{
       pageSpinner.end();
       return r.json();
      })
    
    
    Login or Signup to reply.
  4. You can use the abort method of the XMLHttpRequest object and then set a timeout to abort the request after 30 seconds. Like so:

    var requestObj = $.ajax({
        ...
    )};
    setTimeout(function(){
        requestObj.abort();
    }, 30 * 1000); // 30 seconds
    

    See XMLHttpRequest.abort reference


    Another approach is to have the success function cease to work after 30 seconds by setting a status flag to ‘out of time’. See comments in the code below:

    /* Use this variable to check whether we are within 30 minutes. This can be:
        'still in time': the original state, waiting for a response
        'out of time': we passed the 30 minutes, don't do anything with a possible success response and call the reject function
        'already resolved': resolved within 30 minutes, no need to reject after 30 minutes
    */
    var timeStatus = 'still in time';
    /* Time until timeout */
    var timeoutTime = 30 * 1000; //30 seconds
    /* Save the request */
    var theRequest = $.ajax({
        ...
        /* In success we must check if we are still in time */
        success: function(data, textStatus, jqXHR){
            
            if(timeStatus === 'out of time'){
                // BAIL OUT. Do nothing since this has alreay been rejected in the setTimeout function. */
                console.log("bailing out. 30 minutes exceeded");
                return false;
            }
            else{
                /* Set to 'already resolved' so our setTimeout function doesn't reject. */
                timeStatus = 'already resolved';
                resolve(data, textStatus, jqXHR);
            }
        },
        /* In error we do the same 'out of time' check */
        error: function (jqXHR, errorCode, error){
            if(timeStatus === 'out of time'){
                // BAIL OUT. Do nothing since the request has alreay been rejected in the setTimeout function. */
                console.log("bailing out. 30 minutes exceeded");
                return false;
            }
            else{
                /* Set to 'already resolved' so our setTimeout function doesn't reject. */
                timeStatus = 'already resolved';
                reject(jqXHR, errorCode, error);
            }
        }
    });
    /* After 30 minutes set the timeStatus to 'out of time' so we can cut off incoming ajax responses. */
    setTimeout(function(){
        if(timeStatus !== 'already resolved'){
            timeStatus = 'out of time';
            reject(theRequest, 'timeout','');
        }
    }, timeoutTime); 
    
    Login or Signup to reply.
  5. performAuthorizePaymentRequest: function (payment) {
    
    // you can start a loading on your view in here like this
    // $('.fix-loading').show('fast');
          let promises_await = [];
          promises_await.push(
                        $.ajax({
                            type: 'POST',
                            url: encodedContextPath + '/checkout/authorise_order',
                            data: JSON.stringify(payment),
                            dataType: 'json',
                            contentType: 'application/json',
                            success: resolve,
                            error: reject,
    // I don't know are these lines below are necessary for your payment or not
                            //always: function () {
                            //     pageSpinner.end() 
                            //},
                            //timeout: 30000 /* call this.session.completePayment() within 30 seconds or the payment is cancelled */
                        });
                 );
    
    $.when.apply($, promises_await).then(function () {
    // if all requests in ajax complete and receive answer, do something here
    
    // also if use loading you can hide it here like this and also in catch
    // $('.fix-loading').hide('fast');
    }).catch(function () {
    // also if use loading you can hide it here like this and also in catch
    // $('.fix-loading').hide('fast');
    //error handling
    });
    }
    
    Login or Signup to reply.
  6. You can using promise.race().

    The Promise.race() method returns a promise that fulfills or rejects
    as soon as one of the promises in an iterable fulfills or rejects,
    with the value or reason from that promise.

    Example:

    const timeout = (prom, time) =>
        Promise.race([prom, new Promise((_r, rej) => setTimeout(rej, time))]);
    

    Use:

    // resolves in 500 ms
    const fn = async () => {
        await new Promise((res) => setTimeout(res, 500));
        return "p2";
    }
    
    // finishes before the timeout
    const result = await timeout(fn(), 1000);
    // result = p2
    
    // timeouts in 100 ms
    await timeout(fn(), 100);
    // error
    

    ref: anonystick.com

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search