在JS中链接异步函数调用?

时间:2016-06-25 00:07:06

标签: javascript jquery asynchronous

所以我对JavaScript很新,并且知道异步函数调用。我做了很多研究,发现如果你想连续运行异步调用,你可以使用回调函数和promises。现在我已经了解了如果只运行一些异步函数,这两个实现是如何有用的。我试图解决一个完全不同的动物;至少据我所知。我目前正在建立一个网站,需要看起来像是在为自己写文字。只是为了让每个人都知道我的JS代码,这里是写入网页的功能(我是相当新的,所以如果你认为你有更好的解决方案,一个小描述的例子将不胜感激):< / p>

&#13;
&#13;
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);
&#13;
<div id="someRandomDiv">

</div>
&#13;
&#13;
&#13;

有了这个,我试图一行接一行地写一行文字到一个网页。基本上我用来编写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()中的for循环将在一个或两个异步函数调用结束之前完成执行。我问这个错误是否有一个理智的解决方案,我有n个异步调用,我需要在下一个触发之前执行一个。值得一提的是,我不想在上面运行这个循环并添加另一个变量,只记录我应该延迟每次写入的段落。我更希望是否有一种编程方式在下一个函数被调用之前强制执行该函数。感谢您阅读这个怪物问题!

2 个答案:

答案 0 :(得分: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);
}

当完整的callback写入pageText时,会调用element。请注意,它还会在完成后清除间隔计时器,从而避免事件循环泄漏。

然后,您需要使用此回调链接异步调用。您可以使用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());
}

答案 1 :(得分:1)

除了Bo的回答,这里是你如何用promises做的(因为承诺很棒!)。它有点高级,但我也发现它更优雅(对字符串的数组方法调用,减少)。

我也使用了箭头功能。如果您需要支持旧浏览器,则可能需要使用常规功能替换它们。

&#13;
&#13;
// 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());
&#13;
<div id="the-element"></div>
&#13;
&#13;
&#13;