承諾

2015-08-31 135 views
2

我有以下代碼:承諾

function load(lab, el) { 
    return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]) 
    .then(function(responses) { 
     parse(responses[0], el, responses[1]); 
    }); 
} 

function parse(html, parent, context) { 
    var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; }); 

    for (var i = 0; i < children.length; i++) { 
    var child = children[i]; 

    if (child.tagName.indexOf('-') >= 0) { 
     load(child.tagName.toLowerCase(), parent); 
    } 
    else { 
     var parsedNode = parseNode(child, context); 
     parent.appendChild(parsedNode); 
     if (child.hasChildNodes()) 
     parse(child, parsedNode, context); 
    } 
    } 
} 

基本上,這是它應該做的:

  1. 在我app.js我稱之爲load功能,也將導入2文件,一個html和一個js,當這些請求的承諾是fullfilled,它調用了一個名爲parse功能,這將循環到HTML和將解析一些字符串,在JS文件中聲明的類。
  2. 在循環內部,可以找到一些自定義標記,例如<my-element>,然後它將嘗試加載my-element.htmlmy-element.js,並且它也會在該HTML內循環。
  3. 正如你可以在代碼中看到,我通過家長和背景,所以在完成所有的環圈後,「大」家長應該有它裏面的所有其他組件。

問題

由於load函數返回一個承諾,我打電話同步,它立即返回,因爲這個原因,孩子不放在正確的家長裏。

如果我是用C#或者用ES7 asyncawait這樣的關鍵字來做這件事,那很簡單。但我不知道怎樣才能異步調用load函數。任何猜測?

+0

我看不出它是如何將它放入不正確的父項,因爲它是「異步」。 – MinusFour

+0

呃,如果你也在過濾文本節點,你不應該訪問他們的'.tagName'而不檢查它是否是一個元素。 – Bergi

+0

@Bergi其實我的代碼比這個要大得多。我已經簡化了它的上下文我的問題 - 我檢查節點的類型等:) – Buzinas

回答

3

如果一個函數是異步的,它應該返回一個promise。總是。即使(或:尤其是)在then回調。

如果您在循環產生多重的承諾,你可以通過Promise.all等待着他們:

function load(lab, el) { 
    return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]) 
    .then(function(responses) { 
     return parse(responses[0], el, responses[1]); 
// ^^^^^^ 
    }); 
} 

function parse(html, parent, context) { 
    var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; }); 

    return Promise.all(children.map(function(child, i) { 
//^^^^^^^^^^^^^^^^^^ 

    if (child.tagName.indexOf('-') >= 0) { 
     return load(child.tagName.toLowerCase(), parent); 
// ^^^^^^ 
    } else { 
     var parsedNode = parseNode(child, context); 
     parent.appendChild(parsedNode); 
     if (child.hasChildNodes()) 
     return parse(child, parsedNode, context); 
//  ^^^^^^ 
    } 
    })); 
} 

如果我這樣做,在C#中,例如,或與ES7異步和等待關鍵字,會很容易。但我不知道如何可以異步調用load函數

是的,你真的應該考慮使用這些。或者您可以使用ES6生成器函數和runner(由許多流行的promise庫提供)來模擬它們。但是,無論如何你都在使用一個轉譯器,對嗎?

load將與他們相當容易:

async function load(lab, el) { 
    var responses = await Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]); 
    return parse(responses[0], el, responses[1]); 
} 
+0

我正在使用Typescript,它沒有異步/等待已經實現:( – Buzinas

+0

它有發電機嗎? – Bergi

+0

還沒有,但它似乎很快會! – Buzinas

0

我覺得這是你聽錯了:

if (child.tagName.indexOf('-') >= 0) { 
     load(child.tagName.toLowerCase(), parent); 
    } 

您傳遞的child對象的parentgrandchild對象的parent。您可能需要通過child作爲grandchild的父級。

+0

代碼中沒有'邏輯'錯誤。事實上,如果我沒有使用任何自定義標籤,它就像一個魅力,問題只發生在我需要加載其他htmls/js時,並且在執行'parse'方法之前立即執行該函數。 – Buzinas

1

可以使用。降低以實現:

function load(lab, el) { 
    return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]) 
    .then(function(responses) { 
     return parse(responses[0], el, responses[1]); 
    }); 
} 

function parse(html, parent, context) { 
    var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; }); 

    // What this return is a promise chained through all of its children 
    // So until all children get resolved, this won't get resolved. 
    return children.reduce(function(promise, child, idx) { 
    var childPromise = null; 
    if (child.tagName.indexOf('-') >= 0) { 
     // Start to load the contents, until childPromise is resolved, the final 
     // can't be resolved. 
     childPromise = load(child.tagName.toLowerCase(), parent); 
    } else { 
     var parsedNode = parseNode(child, context); 
     parent.appendChild(parsedNode); 
     // If it has child, also make it return a promise which will be resolved 
     // when child's all children parsed. 
     if (child.hasChildNodes()) { 
     childPromise = parse(child, parsedNode, context); 
     } 
    } 

    // It's either null, which means it'll be resolved immediately, 
    // or a promise, which will wait until its childs are processed. 
    return promise.then(function() { 
     return childPromise; 
    }); 
    }, Promise.resolve()); 
} 

然後,當iteratin通過孩子,它會保留鏈接的承諾,每個孩子可以負載或獨立解析,直到所有的孩子是解決,從parse承諾退貨得到解決。所以,你現在可以使用它像:

parse(THE PARAM OF ROOT).then(function() { 
    // All the big parents's children are now parsed. 
    console.log('All done'); 
}); 

編輯:爲Bergi表明,Promise.all是更好然後。降低,因爲它會立即拒絕任何時候的孩子(孫子)失敗。和The more acceptable answer一樣,我只是給它一個鏈接,而不是添加.all版本。

JavaScript Promises#chaining也可以幫助你。

+1

你應該真的考慮做'return Promise.all([promise,childPromise])'而不是'.then()'的東西。這樣,它會立即拒絕錯誤。 – Bergi

+0

@Bergi,謝謝你的提醒,在寫答案的時候忘了''all'! – fuyushimoya

0

在最後,這是簡單的,比我想象的要:

function load(lab, el) { 
    return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]) 
    .then(function(responses) { 
     return parse(responses[0], el, responses[1]); // return here 
    }); 
} 

function parse(html, parent, context) { 
    var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; }); 

    for (var i = 0; i < children.length; i++) { 
    var child = children[i]; 

    if (child.tagName.indexOf('-') >= 0) { 
     return load(child.tagName.toLowerCase(), parent); // return here 
    } 
    else { 
     var parsedNode = parseNode(child, context); 
     parent.appendChild(parsedNode); 
     if (child.hasChildNodes()) 
     parse(child, parsedNode, context); 
    } 
    } 
} 

由於我的分析必須是同步的(因爲訂單等),唯一我需要的是等待load函數完成,然後再回到parse,我改變的唯一的事情是直接調用parse函數load之一,反之亦然,我現在使用return,因爲它會在返回給調用者之前等待執行。我結束了創建我的自定義元素的副本,並將其附加到父,調用加載函數傳遞:


奏效,它甚至對我的使用情況較好的其他事情。這樣做,我可以加載所有的孩子異步,沒有不被附加到DOM的問題。

執行速度更快,可讀性更好!

+0

好吧,它看起來不像那樣工作,它可能適用於你的簡單示例測試用例,但它通常不會。你幾乎不想擺脫循環(?),並且對'parse'的遞歸調用也可能(也可能)是異步的,所以你必須不要忘記把它照顧。 – Bergi