Reputation: 35
The below function takes an array of words calls an api for info on each word and saves the data in the definitions
object. I've used a promise to wait for a server response before returning any data.
function define(arr) { //pyramid of doom at the expense of adding abstraction
return new Promise(function(resolve, reject) {
var client = [];
var definitions = {};
for (var i = 0, len = arr.length; i < len; i++) {
(function(i) {
client[i] = new XMLHttpRequest();
client[i].onreadystatechange = function() {
if (client[i].readyState === 4 && client[i].status === 200) {
definitions[arr[i]] = JSON.parse(client[i].responseText);
if (Object.keys(definitions).length === arr.length) {
resolve(definitions);
}
}
};
client[i].open('GET', 'http://api.wordnik.com:80/v4/word.json/' + arr[i] +
'/definitions?limit=1&includeRelated=false&sourceDictionaries=all&useCanonical=false&includeTags=false&api_key=737061636520696e74656e74696f6e616c6c7920626c616e6',
true);
client[i].send();
})(i);
}
});
}
When every element with the arr
argument is a word in the Wordnik API database the above program works fine. When a non word is passed in however the program breaks down. I would like to make it so any non word is either omitted or displays "definition not found."
The resolve function creates a click eventhandler and if a non word is passed to define
then when the event handler is triggered by a click, the error "Uncaught TypeError: Cannot read property '0' of undefined" appears for a line with the code obj[this.id][0].text
.
I tried adding in this conditional to the onreadystate
anonymous function:
if (client[i].responseText[0] === undefined) {
client.responseText[0] = {
word: arr[i],
text: 'Definition not found'
};
but it doesn't fix anything.
Upvotes: 0
Views: 250
Reputation: 225281
responseText
is a string. You’re parsing it as JSON here:
definitions[arr[i]] = JSON.parse(client[i].responseText);
The problem of a definition not being found isn’t in your request code at all. Wherever you’re using the result of the promise is where you should be checking whether the definition array is empty. The place where the error occurs sounds promising:
… the error "Uncaught TypeError: Cannot read property '0' of undefined" appears for a line with the code
obj[this.id][0].text
.
Before reading obj[this.id][0].text
, ensure that obj[this.id].length !== 0
.
var definitions = obj[this.id];
var something =
definitions.length === 0 ?
'Definition not found' :
definitions[0].text;
Also, you’re reimplementing Promise.all
by counting keys. I’d suggest making a separate function to define one word.
var API_KEY = '737061636520696e74656e74696f6e616c6c7920626c616e6';
function queryString(map) {
return '?' +
Object.keys(map)
.map(function (key) {
return key + '=' + encodeURIComponent(map[key]);
})
.join('&');
}
function defineWord(word) {
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
var uri =
'http://api.wordnik.com/v4/word.json/' + encodeURIComponent(word) +
'/definitions' + queryString({
limit: 1,
includeRelated: false,
sourceDictionaries: 'all',
useCanonical: false,
includeTags: false,
api_key: API_KEY,
});
request.addEventListener('error', reject);
request.addEventListener('load', function () {
resolve(this.response);
});
request.responseType = 'json';
request.open('GET', uri, true);
request.send(null);
});
}
function define(words) {
return Promise.all(words.map(defineWord))
.then(function (results) {
var definitions = {};
results.forEach(function (result, i) {
definitions[words[i]] = result;
});
return definitions;
});
}
Upvotes: 1
Reputation: 1578
Your condition is correct, but the executed code is not. Just do something like this:
if (client[i].responseText[0] === undefined) {
reject('Word not found');
}
Then in your code make the call to define(arr)
looks something like this:
define(arr).catch(function(error) {
alert(error);
});
This code is only an example, you can make it suitable for you!
Upvotes: 0