【问题标题】:Async / Callstack confusion异步/调用堆栈混淆
【发布时间】:2016-05-25 10:44:39
【问题描述】:

好的,我很确定我知道问题出在哪里,但我终生无法弄清楚如何解决它。

以下代码的工作方式是前端将两个单词发送回服务器,进行一些清理并将字符串分解为数组。然后对该数组进行迭代,为每个单词向 Wordnik API 发出异步请求以获取同义词。发送回客户端的结果数据结构是一个带有{word1: [...synonyms], word2: [...synonyms]} 的对象。

用两个词,这完全符合我想要的 5 次中的 4 次。第五次,第二个词的同义词被应用于第一个词,而第二个词没有数据。显然,发送的字数越多,数据混乱的情况就越多。

所以,我很确定这是一个调用堆栈问题,但我不知道如何解决它。我一直在想是否将 wordnikClient 包装在 setTimeout(..., 0);这是朝着正确方向迈出的一步,但感觉就像我误用了这种模式。有什么智慧之言吗?

编辑:https://github.com/ColinTheRobot/tweetsmithy-node/blob/master/server.js 这是以前的版本,它具有相同的异步问题。我最初用 Promise 设计了它,但在过去的几天里意识到它并没有真正做任何事情/我也可能误用了它,所以现在把它拿出来。

app.get('/get-synonyms', (req, res) => {
    var tweetWords = sanitizeTweet(req.query.data);
    getDefs(tweetWords, res);
});

var getDefs = function(tweetWords, res) {
    var i = 0;
    var serialized = {};

    tweetWords.forEach((word) => {
        wordnikClient(word, (body) => {
            var wordToFind = tweetWords[i];
            var shortenedWords = [];
            i++;

            if (body[0]) {
                shortenedWords = _.filter(body, (syn) => {
                    return syn.length < wordToFind.length;
                });
                serialized[wordToFind] = shortenedWords;
            }

            if (tweetWords.length == i) {
                res.send(serialized);
            }
        });
    });
}

var sanitizeTweet = function(tweet) {
    var downcasedString = tweet.toLowerCase();
    var punctuationless = downcasedString.replace(/[.,-\/#!$%\^&\*;:{}=\-_`~()]/g,"");
    var finalString = punctuationless.replace(/\s{2,}/g," ");
    return finalString.split(' ');
}

var wordnikClient = function(word, callback) {
    var url = `http://api.wordnik.com:80/v4/word.json/${word}/relatedWords?useCanonical=false&relationshipTypes=synonym&limitPerRelationshipType=10&api_key=${process.env.WORDNIK_API_KEY}`
    console.log('calling client');
    request(url, (err, response, body) => {
        if (!err && response.statusCode == 200 && response.body != '[]') {
            callback(JSON.parse(body)[0].words);
        } else if (!err && response.statusCode == 200 && response.body == '[]') {
            callback([false]);
        }
    });
}

【问题讨论】:

  • 如果第二个请求在前,它会让var wordToFind = tweetWords[i];成为第一个单词,这是错误的

标签: javascript node.js asynchronous callstack


【解决方案1】:

是的,发生的事情是您的第二个异步调用首先完成,因为 fo

if (tweetWords.length == i) {
            res.send(serialized);
        }
    });

正在返回给客户端。一种替代方法是使用https://github.com/caolan/async 来协调您的异步调用,但我建议您将wordnikClient 转换为promise,然后使用Promise.all 来控制res.send

var wordnikClient = function(word) {
    var url = `http://api.wordnik.com:80/v4/word.json/${word}/relatedWords?useCanonical=false&relationshipTypes=synonym&limitPerRelationshipType=10&api_key=${process.env.WORDNIK_API_KEY}`
    console.log('calling client');
    return new Promise( (resolve, reject) => {
      request(url, (err, response, body) => {
        if (!err && response.statusCode == 200 && response.body != '[]') {
            resolve(JSON.parse(body)[0].words);
        } else if (!err && response.statusCode == 200 && response.body == '[]') {
            reject([false]);
        }
    });
});

Promise.all(tweetWords.map((word) => wordnikClient(word)))
.then(serialized => res.send(serialized))
.catch(err  => res.status(500).send(err))

我可能在此过程中丢失了一些功能,但您可以重新添加它

【讨论】:

  • 谢谢@simon,这正是我需要做的。我仍在努力解决它。理论上,我理解/理解调用堆栈没有按照我想要的顺序运行方法,并且可以通过控制台日志记录看到它。
【解决方案2】:

getDefs 内部的异步回调做什么尚不清楚。 i 变量计算回复的顺序,所以我不明白为什么要使用它来索引 tweetWords。我建议您只使用word。使用 Promises 可以做出更清晰的解决方案:

function getDefs(tweetWords, res) {
    var serialized = {};

    Promise.all(tweetWords.map(word => {
      return wordnikClientAsync(word).then(body => {
        if (body[0]) {
          serialized[word] = _.filter(body, syn => syn.length < word.length);
        }
      });
    })).then(() => {
      res.send(serialized);
    }, () => {
      res.send("Error");
    });

    function wordnikClientAsync(word) {
        return new Promise(resolve => wordnikClient(word, resolve));
    }
}

【讨论】:

    【解决方案3】:

    tweetWords[i]; 更改为word,因为该变量在回调之外并且迭代​​当前可能不会运行。

    【讨论】:

    • 字!哈哈。是的……呃。我仍然需要解决请求顺序,但这解决了我的数据变得混乱的问题。太愚蠢了。谢谢
    猜你喜欢
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    • 2019-08-03
    • 2018-10-07
    • 2013-03-31
    相关资源
    最近更新 更多