JQuery.when() 和 then() 不适用于异步事件

JQuery.when() and then() not working with asynchronous event

我正在尝试从文本文件中读取行,从文件中选择一行并将其插入到 html 页面上的 div。

var chosenWord="original word";

function wordFromFile(fileName) {
    $.get(fileName, function(data) {
        var lines = data.split("\n");
        var lineCount = lines.length;
        var randomIndex = Math.floor(Math.random() * ((lineCount-1) + 1));
        var word = lines[randomIndex];
        window.chosenWord = word;
        console.log("wordFromFile: "+chosenWord);
    });
}

function setRandomWord() {
  $.when(wordFromFile("http://mypage.com/words.txt")).then(appendWord());
}

function appendWord() {
  console.log("appendWord: "+window.chosenWord);
  $('.word').text(window.chosenWord);
}

$(function() {
  if($('body').is('.playpage')) {
    setRandomWord();
  }

});


console shows:
appendWord: original word
wordFromFile: particle //when i refresh the page this word changes but appendWord is always "original word"

如何才能使函数 wordFromFile(fileName) 先完成,然后 appendWord 为全局变量 chosenWord 获取相同的值?我不明白为什么 when() 和 then() 函数在我的代码中不起作用。

你必须returnwordFromFile()中的承诺。

function wordFromFile(fileName) {
    return $.get(fileName).then(function(data) {
        var lines = data.split("\n");
        var lineCount = lines.length;
        var randomIndex = Math.floor(Math.random() * lineCount);
        var word = lines[randomIndex];
        window.chosenWord = word;
        console.log("wordFromFile: "+chosenWord);
        return chosenWord;
    });
}

并且,如果您希望最终的 chosenWord 值成为承诺的已解析值,那么您应该 return 它来自 .then() 处理程序。另外,请注意,我将 $.get() 上的回调更改为 .then() 处理程序,以使事情更加一致。通常,一旦您使用了 promise,您就希望在操作中始终如一地使用它们,而不是在 promise 中混合使用老式的回调。这样做会破坏 promises 提供的一些自动错误传播和错误处理功能。

然后,您根本不需要在 setRandomWord() 中使用 $.when() 因为 wordFromFile() 已经 return 是一个承诺,您需要将函数引用传递给.then() appendWord 后没有括号:

function setRandomWord() {
  wordFromFile("http://mypage.com/words.txt").then(appendWord);
}

回顾一下:

  1. Return wordFromFile()
  2. 的承诺
  3. wordFromFile() 中使用 .then() 处理程序而不是 jQuery 回调。
  4. 删除 wordFromFile() 左右的 $.when(),因为不需要。
  5. 将函数引用传递给 .then() 时,移除 appendWord 后的括号。您采用的方式是立即调用 appendWord,而不是让 .then() 处理程序稍后在 promise 解析时调用它。
  6. 你的随机数逻辑不太正确,除非你总是试图避免第一个词。

请记住,承诺没有魔力。当它们内部的异步操作以某种方式完成时,它们不会 "know"。当异步操作手动解决承诺时,他们只知道异步操作何时完成。因此,您不能只在 $.when() 中放置一些异步操作并期望 $.when() 神奇地知道异步操作何时完成(很多人都尝试过这个,所以显然这是人们对 promises 不熟悉的东西似乎在期待)。相反,您将承诺传递给 $.when() 并且异步操作必须解决这些承诺。在您的情况下,您只有一个承诺,因此不需要 $.when()。通常只有当您有多个要等待的承诺时才需要它。当你只有一个承诺时,你可以只调用 .then() 就可以了。

其他注意事项:

  1. 您正在使用全局变量 chosenWord 来传回结果。这不是必需的,通常被认为是一种反模式。
  2. 您的 ajax 调用没有任何错误处理。

此代码:

$(function() {
  if($('body').is('.playpage')) {
    setRandomWord();
  }

});

可以简化为:

$(function() {
  $("body.playpage").each(setRandomWord);
});

如果 body 标签上有 playpage class,那么它将调用 setRandomWord()。如果没有,它不会调用它。


您可以像这样从您的代码中删除 chosenWord 全局变量:

function wordFromFile(fileName) {
    return $.get(fileName).then(function(data) {
        var lines = data.split("\n");
        var randomIndex = Math.floor(Math.random() * lines.length);
        var word = lines[randomIndex];
        console.log("wordFromFile: "+word);
        return word;
    });
}


function setRandomWord() {
  return wordFromFile("http://mypage.com/words.txt")).then(appendWord);
}

function appendWord(word) {
  console.log("appendWord: "+word);
  $('.word').text(word);
}

$(function() {
  $("body.playpage").each(setRandomWord);
});  

这里选择的词是 wordFromFile() return 承诺的解析值,因此它将被传递给任何 .then() 承诺的处理程序。当您将 appendWord 作为 .then() 处理程序传递时,这意味着该词将为您传递给 appendWord。而且,wala 没有使用全局。