【问题标题】:How to wait for 3rd party JavaScript function to return如何等待 3rd 方 JavaScript 函数返回
【发布时间】:2017-05-06 17:32:37
【问题描述】:

给定以下sn-p的代码

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);
});

我不想将按钮连接起来,而是想通过激活 Bootstrap 选项卡事件来触发代码。

$('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);

});

所以我的问题是“如何在更改 childIFrame 的源之前等待此功能完成?”。

empowerInstance.document.hasChanged(hasChangedCallback);

我从概念上理解 Promises 和 Callbacks 的使用,但编写一个能够正确运行的函数是另一回事。

更新

此版本经过重构以消除按钮处理程序,从而提高了可读性。

用法也很重要。当页面首次加载时,它位于选项卡上。此选项卡与 iFrame 中托管的文档相关联。如果用户编辑此文档然后尝试更改选项卡,我想调用脏/保存检查,然后一旦保存,移动到下一个选项卡/文档。还有一种情况是在选项卡/文档之间切换不会导致保存,因为文档不脏。

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);

    });
});

谢谢你, 斯蒂芬

【问题讨论】:

  • 我还没有完全理解你的代码,但一般来说,你的问题的答案是“使用回调函数”。
  • 您可以使用回调函数或承诺。并非所有浏览器都支持 Promise,因此您可以使用 webpack 或 babel 来解决这个问题。

标签: javascript jquery opentext


【解决方案1】:

我已经重构了您的代码,以展示如何使用 Promise 来完成。

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)
  })


});

【讨论】:

  • 我不知道rejects的用法是否正确,我唯一能想到它们会被使用的是如果有错误被捕获。
  • @StephenPatten 这取决于您的用例。个人认为,如果一个保存操作失败,代表这个操作的promise应该被拒绝,但是如果你愿意,你可以resolve这个promise。
  • 应该澄清一下,保存时的拒绝是正确的,但是 hasChanged 在 else 块中很尴尬,这只是意味着我们不调用保存。我是这个东西的菜鸟,如果我没有正确理解用法,很抱歉
  • 我认为不调用 save 才是重点,不是吗? if 语句中的条件为假,那么现在应该发生什么? - 我更新了答案以显示如何添加错误处理
  • 你是对的,如果它不脏就不要调用保存,但这并不意味着它无效,只是不脏所以使用拒绝似乎不对,仅此而已。
【解决方案2】:

你可以使用一些higher order functions 来做你想做的事。不是将hasChangedCallbacksaveCallback 直接传递给empowerInstance.document 方法,而是调用一个返回这些回调的函数,但也传递您自己的回调,一旦所有异步操作最终完成,您将调用该回调完全的。下面是它的样子:

$('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {
    var target = $(e.target).attr("data-EditorUrl"); // activated tab

    onClick_btnSendMessage(function () {
        var childIFrame = $("#editorFrame");
        childIFrame.attr("src", target);
    });
});

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

function getHasChangedCallback(myCallback) {
    return function hasChangedCallback(returnValue,  myCallback) {
        console.log("empowerInstance.document.hasChanged = " + returnValue.isDirty);
        if (returnValue.success === true && returnValue.isDirty === true) {
            empowerInstance.document.save(getSaveCallback(myCallback));
        }
    } 
}

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

        myCallback && myCallback(); // make sure myCallback isn't null before invoking
    }
}

它不是很吸引人,但它应该可以满足您的需求。

【讨论】:

  • 非常接近!似乎目标值始终设置为第一个选项卡,之后就不再更改
  • 奇数。 e.target 的值是否在变化?也许data-EditorUrl 属性的值在异步事情发生时发生了变化?尝试将 target 分配移到回调之外。我将编辑我的答案以反映。
  • 是的,每次点击选项卡时都会更改 e.target。我已经尝试过没有编辑(脏假)和编辑(脏真)相同的行为。
  • 感谢您提供有关这本书的提示,总有一天我会开始阅读的 :)
  • 传递给 onClick_btnSendMessage 的函数永远不会被调用
猜你喜欢
  • 2011-03-04
  • 1970-01-01
  • 2019-02-20
  • 2016-10-16
  • 2021-04-16
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多