异步/调用堆栈混乱
Async / Callstack confusion
好吧,我很确定我知道问题出在哪里,但我想不出如何解决它。
以下代码的工作方式是前端将两个单词发送回服务器,进行一些清理并将字符串分解为数组。然后迭代该数组,为每个单词向 Wordnik API 发出同义词异步请求。返回给客户端的结果数据结构是一个带有 {word1: [...synonyms], word2: [...synonyms]}
.
的对象
用两个词来说,这正是我想要的 5 次中的 4 次。第五次,第二个词的同义词被应用到第一个词,第二个词没有数据。显然,发送的字数越多,数据混乱发生的频率就越高。
所以,我很确定这是一个调用堆栈问题,但我不知道如何解决它。我一直在想,如果我把 wordnikClient 包装在 setTimeout(..., 0) 中;这是朝着正确方向迈出的一步,但感觉我在错误地应用这种模式。那里有什么智慧的话吗?
编辑:https://github.com/ColinTheRobot/tweetsmithy-node/blob/master/server.js 这是之前的版本,它有同样的异步问题。我最初是用 Promise 设计它的,但在过去的几天里意识到,它并没有真正做到 anything/I 也可能误用了它,所以暂时把它拿掉了。
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]);
}
});
}
是的,您的第二个异步调用首先完成,因为 fo
if (tweetWords.length == i) {
res.send(serialized);
}
});
正在返回客户端。一种替代方法是使用 https://github.com/caolan/async 来协调您的异步调用,但我建议您将 wordnikClient
转换为 promises,然后使用 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))
一路上我可能丢失了一些功能,但您可以重新添加
将 tweetWords[i];
更改为 word
,因为变量在回调之外,迭代目前可能不会 运行。
异步回调在 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));
}
}
好吧,我很确定我知道问题出在哪里,但我想不出如何解决它。
以下代码的工作方式是前端将两个单词发送回服务器,进行一些清理并将字符串分解为数组。然后迭代该数组,为每个单词向 Wordnik API 发出同义词异步请求。返回给客户端的结果数据结构是一个带有 {word1: [...synonyms], word2: [...synonyms]}
.
用两个词来说,这正是我想要的 5 次中的 4 次。第五次,第二个词的同义词被应用到第一个词,第二个词没有数据。显然,发送的字数越多,数据混乱发生的频率就越高。
所以,我很确定这是一个调用堆栈问题,但我不知道如何解决它。我一直在想,如果我把 wordnikClient 包装在 setTimeout(..., 0) 中;这是朝着正确方向迈出的一步,但感觉我在错误地应用这种模式。那里有什么智慧的话吗?
编辑:https://github.com/ColinTheRobot/tweetsmithy-node/blob/master/server.js 这是之前的版本,它有同样的异步问题。我最初是用 Promise 设计它的,但在过去的几天里意识到,它并没有真正做到 anything/I 也可能误用了它,所以暂时把它拿掉了。
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]);
}
});
}
是的,您的第二个异步调用首先完成,因为 fo
if (tweetWords.length == i) {
res.send(serialized);
}
});
正在返回客户端。一种替代方法是使用 https://github.com/caolan/async 来协调您的异步调用,但我建议您将 wordnikClient
转换为 promises,然后使用 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))
一路上我可能丢失了一些功能,但您可以重新添加
将 tweetWords[i];
更改为 word
,因为变量在回调之外,迭代目前可能不会 运行。
异步回调在 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));
}
}