2016-06-25 24 views
2

所以我相當新的JavaScript和意識到異步函數調用。我做了相當多的研究,發現如果你想連續運行異步調用,你可以使用回調函數和promise。現在我已經開始瞭解如果只運行一些異步函數,這兩種實現是否有用。我試圖解決一個完全不同的動物;至少就我所知而言。我目前正在構建一個需要顯示的網站,好像它正在向自己寫文本。爲了向大家介紹我的JS代碼,下面是寫入網頁的功能(我相當新,所以如果您認爲自己有更好的解決方案,請點擊以下示例):在JS中鏈接異步函數調用?

function write(pageText, elementId, delay) { 
 
    var element = document.getElementById(elementId); 
 
    var charCount = 0; 
 

 
    setInterval(function() { 
 
    if (charCount > pageText.length) { 
 
     return; 
 
    } else { 
 
     element.innerHTML = pageText.substr(0, charCount++); 
 
    } 
 
    }, delay); 
 
} 
 

 
write("This is an example", 'someRandomDiv', 100);
<div id="someRandomDiv"> 
 

 
</div>

有了這個我想一行文字寫了一個又一個網頁一行。從本質上講,我利用在Java和C#編寫像這樣的代碼:

function writePassage() 
{ 
    var passage=["message one", "message two", "... message n"]; 
    for(var i = 0; i<passage.length; i++) 
    { 
     write(passage[i], 'someRandomDiv', 100); 
    } 
} 

很明顯,因爲這將無法工作,因爲在wirtePassage()循環將完成一個或兩個異步函數調用之前執行結束。我問是否有一個理智的解決方案來解決這個錯誤,我有n個異步調用,並且我需要在下一個觸發之前執行一個執行。值得一提的是,我不想僅僅在上面運行這個循環,並添加另一個變量,以便跟蹤我將拖延每段文字的時間。如果在調用下一個函數之前有一種強制執行函數的編程方式,我寧願選擇。感謝您閱讀這個怪物問題!

+1

*「這是不行的」 *不是它做什麼預期工作或拋出什麼錯誤,正確的解釋。請參閱[問] – charlietfl

+0

https://chainyjs.bevry.me可能會對您感興趣 – balupton

+0

僅供參考 - 忘記清除時間間隔:var interval = setInterval(function(){ if(charCount> pageText.length){ clearInterval(區間); }其他{ element.innerHTML = pageText.substr(0,charCount ++);} } ,延遲);' –

回答

6

有幾件事情你需要做,以實現這個工作。

首先,你的write函數將需要一個異步接口。正如你所說,它可以採取回調或返回一個承諾。以回調看起來是這樣的:

function write(pageText, elementId, delay, callback) 
{ 
    var element = document.getElementById(elementId); 
    var charCount=0; 

    var interval = setInterval(function(){ 
    if(charCount>pageText.length) 
    { 
     clearInterval(interval); 
     callback(); 
    } 
    else 
    { 
     element.innerHTML = pageText.substr(0,charCount++); 
    } 
    }, delay); 
} 

,當全pageText已經寫入element調用callback。請注意,它也會在完成時清除間隔計時器,這可以避免事件循環泄漏。

然後,您需要使用此回調鏈接您的異步調用。您可以使用庫如async這樣做很乾淨:

function writePassage() 
{ 
    var passage=["message one", "message two", "... message n"]; 
    async.series(passage.map(function(text){ 
    return function(done){ 
     write(text, 'someRandomDiv', 100, done); 
    }; 
    })); 
} 

但它也沒有那麼多的麻煩做手工:

function writePassage() 
{ 
    var passage=["message one", "message two", "... message n"]; 

    var writeOne = function() { 

    if (!passage.length) return; 

    var text = passage.shift(); 

    write(text, 'someRandomDiv', 100, writeOne); 
    } 

    // Kick off the chain. 
    writeOne(); 
} 

這只是異步遞歸。歡迎使用JavaScript。 :)

基於承諾的解決方案也可以很乾淨。首先,你需要write一個承諾:

function write(pageText, elementId, delay) 
{ 
    return new Promise(resolve) { 
    var element = document.getElementById(elementId); 
    var charCount=0; 

    var interval = setInterval(function(){ 
     if(charCount>pageText.length) 
     { 
     clearInterval(interval); 
     resolve(); 
     } 
     else 
     { 
     element.innerHTML = pageText.substr(0,charCount++); 
     } 
    }, delay); 
    } 
} 

然後你就可以通過減少創建承諾的

function writePassage() 
{ 
    var passage=["message one", "message two", "... message n"]; 

    passage.reduce(function(chain, text) { 
    return chain.then(function(){ 
     return write(text, 'someRandomDiv', 100, writeOne); 
    }); 
    }, Promise.resolve()); 
} 
+0

神聖的鯉魚什麼一個很好的答案! +1 for'caolan/async'庫存 – Plato

+0

這是迄今爲止我收到的最好的答案之一。感謝豐富而詳細的答案! – user3750761

1

除了博的回答,這裏是你會怎樣用承諾來做(因爲承諾太棒了!)。它有點高級,但我也覺得它更優雅(數組方法調用字符串,減少)。

我也使用了箭頭函數。如果您需要支持舊瀏覽器,則可能需要使用常規功能替換它們。

// Return a promise resolved after time ms. 
 
var wait = (time) => new Promise((resolve) => setTimeout(resolve, time)); 
 

 
function write(pageText, elementId, delay){ 
 
    // Fetch the element. 
 
    var element = document.getElementById(elementId); 
 
    // Empty the element. 
 
    element.innerHTML = ''; 
 
    // Reduce on each character of pageText with a resolved promise 
 
    // as a initialiser, and return the resulting promise. 
 
    return Array.prototype.reduce.call(pageText, (promise, char) => { 
 
    // Chain to the previous promise. 
 
    return promise 
 
     // First wait delay ms. 
 
     .then(() => wait(delay)) 
 
     // Then add the current character to element's innerHTML. 
 
     .then(() => element.innerHTML += char); 
 
    }, Promise.resolve()); 
 
} 
 

 

 
var messages = ["message one", "message two", "... message n"]; 
 
messages.reduce((promise, message) => { 
 
    return promise 
 
    // Write current message. 
 
    .then(() => write(message, "the-element", 100)) 
 
    // Wait a bit after each messages. 
 
    .then(() => wait(400)); 
 
}, Promise.resolve());
<div id="the-element"></div>

+0

我沒有把這個建議的解決方案做成這個,但是你的評論也幫助我瞭解了一個有效的例子的其他實現!我非常感謝你把答案放在你的答案中! – user3750761