IndexDB cursor .onsucess as a promise - javascript

First of all, I can't find a suitable title for this question - please feel free to edit.
I have the following function that reads objects from indexDb,
loadNeededParcels = property => {
const dbResults = [];
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
console.log("open cursor error ", e);
};
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
return dbResults;
}
};
};
Now when I call this function with a simple function call, for example:
console.log(loadNeededParcels('hasData'))
The console log is undefined. I am guessing this happens because the function does not wait for the cursor to finish and return the dbResults variable?
So my question is this - how can I re-write this function as a promise, or rather to wait for the readTransaction.openCursor().onsucess to trigger?
So the expected result is for the function to actually return the values read from the database before exiting.
I am using cursors since the .getAll() the method is not supported in IE.

A simple solution that I ended up using:
loadNeededParcels = property => {
return new Promise((resolve, reject) => {
var readTransaction = this.db
.transaction("parcelData")
.objectStore("parcelData");
readTransaction.openCursor().onerror = e => {
reject(e);
};
const dbResults = [];
readTransaction.openCursor().onsuccess = e => {
const cursor = e.target.result;
if (cursor) {
dbResults.push(cursor.value);
cursor.continue();
} else {
resolve(dbResults);
}
};
});
};

Try something like this. Do not call openCursor twice, that creates two requests.
function loadNeededParcels(db, property) {
return new Promise(function(resolve, reject) {
var results = [];
var tx = db.transaction('parcelData');
tx.onerror = function(event) {
reject(tx.error);
};
var store = tx.objectStore('parcelData');
// Open a cursor over all items
var request = store.openCursor();
request.onsuccess = function(event) {
var cursor = request.result;
if(cursor) {
var value = cursor.value;
if(value) {
// Only append defined values to the array
results.push(cursor.value);
}
cursor.continue();
} else {
resolve(results);
}
};
});
}
loadNeededParcels(db, 'hasData').then(function(results) {
console.log('Results', results);
}).catch(function(error) {
console.error(error);
});

Related

Promise ignoring simple syncronous operation

In a promise, I want to assign a value to the property of several objects created from a class (in a loop), but when executing the function and doing the .then(() => console.log(r)) thing, the r does was not modified to what the promise promised me it would.
Here:
function assignSentenceImageDescription () {
return new Promise((resolve, reject) =>
{
assigningWordsPartOFSpeech().then((r) => {
JSON.parse(r).sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
}
)
resolve(r);
}
);
}
);
}
On the line
sentence.imageDescription = sentenceImageDescription;
I try to assign the image description of each of the sentences iterated over, but when I do
assignSentenceImageDescription().then(r => console.log(r));
the r object still does not have each of its sentences's imageDescription property modified to the value the array sentenceImageDescription has, which is what the assignSentenceImageDescription() function is intended to do.
Refactor your code as follows:
Note: you don't need a Promise constructor since assigningWordsPartOFSpeech returns a Promise that you can work with (and return)
Set sentences = JSON.parse(r).sentences;
Now you can iterate through sentences as you already do, then simply return sentences in the .then - and you're done
function assignSentenceImageDescription() {
return assigningWordsPartOFSpeech().then((r) => {
const data = JSON.parse(r);
data.sentences.forEach((sentence) => {
let adjectiveBeforeNoun = [];
let sentenceImageDescription = [];
sentence.words.forEach((wordInSentence) => {
try {
if (wordInSentence.partOfSpeech[0].wordtype === "n.") {
let imageDescription = adjectiveBeforeNoun.join('') + wordInSentence.partOfSpeech[0].word;
sentenceImageDescription.push(imageDescription)
adjectiveBeforeNoun = [];
} else if (wordInSentence.partOfSpeech[0].wordtype === "superl.") {
adjectiveBeforeNoun.push(wordInSentence.partOfSpeech[0].word + " ")
}
} catch (e) {
console.log("===NOT IN DICTIONARY===")
}
})
sentence.imageDescription = sentenceImageDescription;
});
return data;
});
}

var result = await someFunc() returns an object but I expected list of object

I wrote following function for loading indexeddb. (from IndexedDB 備忘メモ)
I think this function should return Array of object. But, sometimes it returns an object. What are the possibilities of bug ?
Chrome developer tool said type of object was Array during in "load" function. But, after received "records" is type of object.
async function load(dbobj, db, index, range) {
return new Promise(async (resolve, reject) => {
const saves = [];
const req = db.transaction(dbobj.storeName).objectStore(dbobj.storeName).index(index).openCursor(range);
const onfinished = () => {
console.log(`${saves.length} saves found.`);
if (saves.length > 0) {
resolve(saves[saves.length - 1]);
}
};
req.onerror = reject;
req.onsuccess = (ev) => {
const cur = ev.target.result;
if (cur) {
saves.push(cur.value);
cur.continue();
} else {
onfinished();
}
};
});
}
// example of receiving data
var records = await load(dbobj, db, index, range);
you are resolving only the value at the last index! resolve(saves) if you need the entire array;
async function load(dbobj, db, index, range) {
return new Promise(async (resolve, reject) => {
const saves = [];
const req = db.transaction(dbobj.storeName).objectStore(dbobj.storeName).index(index).openCursor(range);
const onfinished = () => {
console.log(`${saves.length} saves found.`);
if (saves.length > 0) {
resolve(saves); // you are resolving only the value at the last index! resolve(saves) if you need the entire array;
}
};
req.onerror = reject;
req.onsuccess = (ev) => {
const cur = ev.target.result;
if (cur) {
saves.push(cur.value);
cur.continue();
} else {
onfinished();
}
};
});
}

IDB: openCursor() is not a function

Well, maybe I do something wrong... But this error is very confusing. I stick with: "Uncaught TypeError: requestChatHistory.openCursor is not a function at IDBOpenDBRequest.dbPromise.onsuccess"
IDBOpenRequest might inherit methods from IDBRequest...
I tried to make request without openCursor() and get the first value from it.
Also tried to re-create database, use different browsers (Chrome, Edge, Firefox)
onUpgrade promise:
dbPromise.onupgradeneeded = function(event) {
let db = event.target.result;
db.createObjectStore('keys', {keyPath: 'userId'});
let chatHistory = db.createObjectStore('messages', { keyPath: "id", autoIncrement:true });
chatHistory.createIndex("chatId", "chatId", { unique: false });
};
There is my function with request:
async function loadSavedMessages(chatId){
let dbPromise = idb.open('clientDB', 3);
dbPromise.onsuccess = function() {
let db = this.result;
let dbTransaction = db.transaction(["messages"]);
let messages = dbTransaction.objectStore("messages");
let index = messages.index('chatId');
let requestChatHistory = index.get(chatId);
requestChatHistory.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (cursor) {
console.log(cursor);
cursor.continue();
}
};
}
}
Function that saves data:
async function saveMessage(chatId, message, userId){
let dbPromise = idb.open('clientDB', 3);
dbPromise.onsuccess = function() {
let db = this.result;
let dbTransaction = db.transaction(["messages"], 'readwrite');
let messages = dbTransaction.objectStore("messages");
let mesObj = {
chatId: chatId,
user: userId,
message: message,
timestamp: Date.now()
};
let save = messages.add(mesObj);
save.onerror = function(event) {
// Handle errors!
console.log("Something went wrong with local DB :(")
};
save.onsuccess = function(event) {
// Do something with the request.result!
console.log(`Message saved, id ${save.result}`);
};
}
}
This is your problem:
let requestChatHistory = index.get(chatId);
requestChatHistory.openCursor().onsuccess = function(event) {
requestChatHistory is an IDBRequest, which has no openCursor method. openCursor is on IDBIndex, like your index variable. So maybe you meant to do something like:
index.openCursor(chatId).onsuccess = function(event) {

Unable to return result from promise

I am trying to read some data from 2 different tables and parse a CSV file before rendering an ejs file.
I can get the data from both tables and from the CSV file but I seem to be unable to return the result.
Pretty sure this is a problem with the way I handle async execution but I fail to see what I am doing wrong.
I've spent the last 2 days reading about this (including the threads around here) and browsing but somehow the answer still escapes me.
First file - usercms.js
app.get('/userscms', function(req, res)
{
existingUsers.getExistingUsers()
.then(function(appUsers)
{
//global users array
//I can display these in my ejs file
globalAppUsers = appUsers;
})
.then(existingUsersAttributesQlik.getExistingUsersAttributesQlik())
.then(function(usersQlikAttributes)
{
//global user attributes array
//undefined data
globalUsersQlikAttributes = usersQlikAttributes;
})
.then(existingSuppliers.parseSuppliersCSV())
.then(function(supplierData)
{
//the result I am expecting
//this prints undefined
console.log(supplierData);
}).then(function()
{
res.render('userscms.ejs',
{
users: globalAppUsers,
attributes: globalUsersQlikAttributes
});
});
});
Second function - getxistingUsers.js (identical to the getExistingUsersAttributesQlik, except for the query)
var userData = [];
var appUsers = [];
(function (exports)
{
exports.getExistingUsers = function ()
{
return promisemysql.createConnection(dbconfig.development).then(function(conn)
{
var result = conn.query("SELECT id, username, firstName, lastName, email, phone, lastLogin, isAdmin, isValid, isPhoneValid, accountCreationDateTime FROM Users");
conn.end();
return result;
}).then(function(rows)
{
return rows;
}).then(function(rows)
{
if (rows.length)
{
userData = [];
appUsers = [];
rows.forEach(function (elem)
{
userData.push(_.toArray(elem));
});
for (i = 0; i < userData.length; i++)
{
var appUser = new appUserModel.AppUser(
userData[i][0],
userData[i][1],
userData[i][2],
userData[i][3],
userData[i][4],
userData[i][5],
userData[i][6],
userData[i][7],
userData[i][8],
userData[i][9],
userData[i][10]);
appUsers.push(_.toArray(appUser));
}
return appUsers;
}
else
{
console.log("NOPE");
return null;
}
}).then(function(appUsers)
{
console.log(appUsers);
return appUsers;
});
};
})(typeof exports === 'undefined' ? this['getExistingUsers'] = {} : exports);
Third file - parseSuppliersCSV.js
var supplierData = [];
var suppliersData = [];
var csvCount = 0;
(function (exports)
{
exports.parseSuppliersCSV = function ()
{
return new Promise(function(resolve, reject)
{
var fileStream = fs.createReadStream("myCSV.csv");
var parser = fastCsv();
csvCount = 0;
supplierData = [];
suppliersData = [];
fileStream
.on("readable", function ()
{
var data;
while ((data = fileStream.read()) !== null)
{
parser.write(data);
}
})
.on("end", function ()
{
parser.end();
});
parser
.on("readable", function ()
{
var data;
while ((data = parser.read()) !== null)
{
if(csvCount >= 1)
{
csvCount++;
var arrayOfStrings = data[0].split(';');
var supplier = new supplierModel.Supplier(arrayOfStrings[0],arrayOfStrings[1]);
suppliersData.push(_.toArray(supplier));
}
else
{
csvCount++;
}
}
})
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return suppliersData;
});
});
};
})(typeof exports === 'undefined' ? this['parseSuppliersCSV'] = {} : exports);
Any ideas what I am doing wrong? Am I approaching this the wrong way?
I'll take a guess here and assume the promise you created should resolve to something...instead of returning a value.
.on("end", function ()
{
console.log("done");
//all OK here
console.log(suppliersData);
//this doesn't seem to return anything
return resolve(suppliersData);
});

Why array.length is not working while array is filled by a async function in javascript

I'm learing WebAudio API. I'm facing a problem with it. Basically things are asynchronous here...so im getting a bit confused. Please help.Here is my code:-
//"use strict";
var sources = new Array();
var actx;
var songs = ['src1.mp3', 'src2.mp3'];
async function start() {
console.log("WELCOME!!");
try {
actx = new AudioContext();
} catch (e) {
console.log('WebAudio api is not supported!!');
}
await getBuffers(actx, songs);
console.log(sources);
console.log(sources.length);
}
function load_song(url) {
let promise = new Promise((resolve, reject) => {
let request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
request.onload = () => {
let audioData = request.response;
resolve(audioData);
}
request.onerror = () => {
reject(new error("Could not load the song:- " + url));
}
request.send();
});
return promise;
}
//creats buffers
async function getBuffers(actx, songs) {
// let buffer_list = new Array();
for (let x = 0; x < songs.length; x++) {
let temp = actx.createBufferSource();
await load_song(songs[x]).then((audioData) => {
actx.decodeAudioData(audioData).then((decodedAudioData) => {
temp.buffer = decodedAudioData;
sources.push(temp);
}).catch((error) => {
console.error(error);
});
});
}
//console.log(buffers.length);
}
async function play() {
//start();
sources[0].start(0);
//sources[1].start(0);
}
function stop() {
sources[0].stop(0);
//sources[1].stop(0);
}
Here in the two lines console.log(sources) and console.log(sources.length). Here the results are. Why console.log(sources.length) is 0?
Please help me........Thank you.
You need to return
actx.decodeAudioData(audioData).then((decodedAudioData) => {
As you are not returning it, you dont await it. Therefore the log appears before the array gets filled, however console.log(sources) is live, so you see the latest change.
Such mistakes are more unlikely to happen when you use await everywhere, and thats also easier to read IMO:
async function getBuffers(actx, songs) {
const sources = []; //mutating a global variable isnt good, so lets make it local
for (const song of songs) { //for...of is so beautiful, why dont you use it?
try { //this is similar to .catch
let temp = actx.createBufferSource();
const audioData = await load_song(song);
const decodedAudioData = await actx.decodeAudioData(audioData);
temp.buffer = decodedAudioData;
sources.push(temp);
} catch(e){ console.error(e); } //okay that does not count as an error handler...
}
return sources; //getBuffers implies that it returns sth
}
You should change your getBuffers code for something like this
async function getBuffers(actx, songs) {
try {
for (let song of songs) {
let temp = actx.createBufferSource();
var audioData = await load_song(songs[x])
var decodedAudioData = await actx.decodeAudioData(audioData)
temp.buffer = decodedAudioData;
sources.push(temp);
}
} catch (e) { console.log(e) }
}

Categories