我是承诺的新手。我知道一个是如何工作的。
我有一个函数将带有外部引用(图像,样式,脚本)的html文档转换为嵌入了这些资源的文件。它需要从异步源(JSZip实例)读取资源。
我希望函数只在所有promises zip.file().async().then(...)
被解析后才返回。例如。有一个domDocument
,然后对它执行一系列异步步骤,然后选择修改后的domDocument
。可以修改调用此函数的区域以使用then
(例如processFile(folder,zip,node,dom).then(function(result)
...如果需要。
// folder is set elsewhere
// zipObject is a JSZip instance
// node is an xml document which contains path references to files mentioned in the html
// domDocument is a DOMParser instance which is the html file
// these are the queryselectors for elements I want to match and replace in the html
[
{"selector":"script[src]:not([src*='//'])","attribute":"src","type":"string"},
{"selector":"link[href$='.css']:not([href*='//'])","attribute":"href","type":"string"},
{"selector":"img:not([src*='//'])","attribute":"src","type":"base64"},
{"selector":"a[target='_blank']:not([href*='://'])","type":"link"}
].forEach(function(element) {
// grab all instances of a particular selector
[].forEach.call(domDocument.querySelectorAll(element.selector), function(instance) {
if (element.type === "link") { // replace link with plain text
var tNode = document.createTextNode(instance.textContent);
instance.parentNode.replaceChild(tNode, instance);
} else { // need to load from the linked document
var value = unescape(instance.getAttribute(element.attribute)),
path = null;
// images that are already encoded can be ignored
if (element.type==="base64" && (-1!==value.indexOf("data:image"))) return;
if (value.indexOf("../")==0) { // resolve dependancy filename
var dep = node.parentNode.querySelector("dependency");
if (null !== dep) {
var ref = node.parentNode.parentNode.querySelector("resource[identifier='" + dep.getAttribute("identifierref") + "']");
if (null !== ref) {
path = ref.querySelector("file[href]").getAttribute("href");
}
}
} else { // resolve path from filename
path = folder + value;
}
// perform the (asynchronous) modification of the document
zipObject.file(path).async(element.type).then(function success(content) {
if (element.attribute === "href") {
// <link href="file.css"> ==> <style>(file contents)</style>
var style = document.createElement("style");
style.appendChild(document.createTextNode(content));
instance.parentNode.replaceChild(style,instance);
} else if (element.type === "base64") {
// <img src="file.gif"> ==> <img src="data:image/gif;base64,(image-contents)">
var extn = value.substr(value.lastIndexOf(".") + 1).toLowerCase();
instance.setAttribute("src", "data:image/" + extn + ";base64," + content);
} else {
// <sript src="file.js"></script> => <script>(file-contents</script>)
instance.removeAttribute(element.attribute);
instance.appendChild(document.createTextNode(content));
}
}, function error (e) {
console.log("ConvertZipForHtml Error", e);
// leave it alone I guess
});
}
});
});
// want this to return the MODIFIED version of the string
return domDocument;
我可以看到第一个数组的每次迭代都需要是一个promise,并且整个函数只应在所有这些promises解析后返回,但我无法弄清楚如何
/编辑:
我想我明白了(感谢@Bergi)。如果我做了一个承诺,它返回了每个只在JSZip的异步函数解析之后解析的项的承诺,那么它似乎异步工作,我的外部函数可以在所有promise完成后使用.then()。
function myFunction(args) {
function replaceElements(elements) {
return Promise.all(elements.map(selectElements));
}
function selectElements(element) {
return new Promise(function (resolve, reject) {
var myNodeList = domDocument.querySelectorAll(element.selector);
for (var i = myNodeList.length; i--;) { // (generally is faster than forwards!)
var instance = myNodeList[i];
// -- snip --
zipObject.file(path).async(element.type).then(function success(content) {
// -- snip --
resolve(instance);
})
}
});
}
return replaceElements([
{"selector":"script[src]:not([src*='//'])", "attribute":"src", "type":"string"},
{"selector":"link[href$='.css']:not([href*='//'])", "attribute":"href", "type":"string"},
{"selector":"img:not([src*='//'])", "attribute":"src", "type":"base64"},
{"selector":"a[target='_blank']:not([href*='://'])", "type":"link"}
]);
}
仍然抓住异步和承诺中的概念