javascriptjqueryopentext

How to wait for 3rd party JavaScript function to return


Given the following snippet of code

var empowerInstance = null;

function onClick_btnSendMessage() {
    var childIFrame = window.document.getElementById("editorFrame");
    if (!empowerInstance) {
        empowerInstance = EditorAPI.getInstance(childIFrame.contentWindow, window.location.origin);
    }
    empowerInstance.document.hasChanged(hasChangedCallback);
}

function hasChangedCallback(returnValue) {
    console.log("empowerInstance.document.hasChanged = " + returnValue.isDirty);
    if (returnValue.success === true && returnValue.isDirty === true) {
        empowerInstance.document.save(saveCallback);
    }
}

function saveCallback(returnValue) {
    console.log("empowerInstance.document.save = " + returnValue.success);
    if (returnValue.success === false) {
        console.log(returnValue.message);
    }
}

window.addEventListener("DOMContentLoaded", function (event) {
    console.log("DOM fully loaded and parsed");
    if (typeof location.origin === "undefined")
        window.location.origin = window.location.protocol + "//" + window.location.host;
    document.getElementById("btnSendMessage").addEventListener("click", onClick_btnSendMessage);
});

Instead of wiring the button up , I'd like to fire the code from the activation of a Bootstrap tab event.

$('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {

    onClick_btnSendMessage(); // Naive way, as this does not wait

    var target = $(e.target).attr("data-EditorUrl"); // activated tab
    var childIFrame = $("#editorFrame");
    childIFrame.attr("src", target);

});

So my question is "How do I wait on this function to complete before changing the source of childIFrame?".

empowerInstance.document.hasChanged(hasChangedCallback);

I conceptually understand the use of Promises and Callbacks, but writing one that functions correctly is a different story.

UPDATED

This version is refactored to eliminate the button handler, thus improving readability.

The usage is also important. When the page loads for the first time it is positioned on a tab. This tab is associated to a document that is hosted in an iFrame. If the user edits this document then tries to change tabs, I'd like to invoke the check for being dirty/save, then once saved, move to the next tab/document. There is also the case that switching between tabs/documents won't cause a save because the document is not dirty.

var empowerInstance = null;

function hasChangedCallback(returnValue) {
    console.log("empowerInstance.document.hasChanged = " + returnValue.isDirty);
    if (returnValue.success === true && returnValue.isDirty === true) {
        empowerInstance.document.save(saveCallback);
    }
}

function saveCallback(returnValue) {
    console.log("empowerInstance.document.save = " + returnValue.success);
    if (returnValue.success === false) {
        console.log(returnValue.message);
    }
}

$(function () {

    if (typeof location.origin === "undefined") {
        window.location.origin = window.location.protocol + "//" + window.location.host;
    }

    $('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {

        var childIFrame = $("#editorFrame");
        if (!empowerInstance) {
            empowerInstance = EditorAPI.getInstance(childIFrame[0].contentWindow, window.location.origin);
        }
        empowerInstance.document.hasChanged(hasChangedCallback);// Need to wait for completion 

        var target = $(e.target).attr("data-EditorUrl"); // activated tab
        childIFrame.attr("src", target);

    });
});

Thank you, Stephen


Solution

  • I've refactored your code to show how this can be done using promises.

    function onClick_btnSendMessage() {
      var childIFrame = window.document.getElementById("editorFrame");
      if (!empowerInstance) {
        empowerInstance = EditorAPI.getInstance(childIFrame.contentWindow, window.location.origin);
      }
      var doc = empowerInstance.document;
      return hasChanged(doc).then(function() { return save(doc) })
    }
    
    
    function hasChanged(doc) {
      return new Promise(function(resolve, reject) {
        doc.hasChanged(function(returnValue) {
          if (returnValue.success === true && returnValue.isDirty === true) {
            resolve(returnValue)
          } else {
            reject(returnValue)
          }
        })
      })
    }
    
    function save(doc) {
      return new Promise(function(resolve, reject) {
        doc.save(function(returnValue) {
          if (returnValue.success === false) {
            console.log(returnValue.message);
            reject(returnValue)
          } else {
            resolve(returnValue)
          }
        })
      })
    }
    
    // ------
    
    $('a[data-toggle="tab"]').on("shown.bs.tab", function(e) {
    
      onClick_btnSendMessage().then(function() {
        var target = $(e.target).attr("data-EditorUrl"); // activated tab
        var childIFrame = $("#editorFrame");
        childIFrame.attr("src", target);
    
      }).catch(function(error) {
         // handle the error
         console.error('Error!', error)
      })
    
    
    });