异步/ Callstack混乱

时间:2016-02-14 16:03:23

标签: javascript node.js asynchronous callstack

好吧,我很确定我知道这是什么问题,但我不能为我的生活找到解决方法。

以下代码的工作方式是前端将两个单词发送回服务器,进行一些清理并将字符串分解为数组。然后迭代该数组,对每个单词的异步请求到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]);
        }
    });
}

3 个答案:

答案 0 :(得分:1)

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

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

正在返回客户端。另一种方法是使用https://github.com/caolan/async来协调您的异步调用,但我建议您将wordnikClient转换为承诺,然后使用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))

我可能在途中丢失了一些功能,但你可以重新添加

答案 1 :(得分:1)

异步回调在PS1='\u@\h:`echo $(basename $PWD) | cut -c 1-15`\$ ' 中的作用并不清楚。 getDefs变量计算回复的顺序,因此我不知道为什么要使用它来索引i。我建议您只使用tweetWords。使用Promises可以做出更清晰的解决方案:

word

答案 2 :(得分:0)

更改tweetWords[i];的{​​{1}},因为该变量位于回调之外,迭代可能暂时无法运行。