Reputation: 365
I have an array that has an image id, name and the image itself (base64). In some cases the id will match, so it needs use a different procedure with a revision number for a parameter. But they have to run sequentially, so as to know whether an id already exists or not.
I have a promise that does all this and would need to be called each time, but I can't seem to figure out the best way to do this. Some ideas online are using "reduce" but not sure how that works.
The code is:
function resetImagesToPouch(image, id, imageName) {
return new Promise(function (resolve, reject) {
var rev;
var imageType;
// check if attachment already exists
DB_TaskImages.get(id, { attachments: true }).then(function (doc) {
rev = doc._rev; // Get the revision number since we are updating and adding attachment to existing doc.
DB_TaskImages.putAttachment(id, imageName, rev, image, "image/jpeg").then(function () {
console.log("Attachment added successfully");
return;
}).then(function () {
resolve();
}).catch(function (err) {
reject(err);
});
return;
}).catch(function (err) {
if (err.status = '404') { // if this is a new document - add new attachment
//imageType = GetImageTypeFromBase64(image);
DB_TaskImages.putAttachment(id, imageName, image, "image/jpeg").then(function () {
console.log("Attachment added successfully");
return;
}).then(function () {
resolve();
}).catch(function (err) {
reject(err);
})
}
});
}); // end promise
}
I have an array of images the I need to refresh my table with such as:
images = {
{id = "111", imageName='fighter.jpg', image='aaaaaddddd'},
{id = "111", imageName='horse.jpg', image='aaasdfssdaaddddd'},
{id = "112", imageName='cat.jpg', image='aasdfaaadsdfsfdddd'},
{id = "113", imageName='dog.jpg', image='aaasdfasfaaaddddd'},
{id = "1234", imageName='apple.jpg', image='aaaasdfasaaddddd'}
}
I don't know how many will be in the array or what the values would be. How would I do this? I can't use Promise.all (I don't think) as they can't fire of together but the .get and .put are async and I need the each promise to finish. I can't use thenables as I don't know how many there will be.
Thanks
The answer below worked fine except it is part of a chain and exited the code before it finished.
}).then(function () {
return ajaxCallForJsonTwoResults(URI_WorkIssues);
}).then(function (args) {
args[1].reduce(function (promise, image) {
return promise.then(function () {
return resetImagesToPouch(image);
});
}, Promise.resolve(args)).then(function () {
console.log('all done with images');
});
}).then(function (args) {
return callBulkDocLoad(DB_WorkIssues, args[0]);
}).then(function () {
In the code, I am passing args (which has two arrays in it - args[0] and args[1]). I am handling two databases here so I needed two responses. The problem is that when I come back from my ajax call, I use args[1] for the first promise and args[0] for the other. But since I come back from 1st promise prematurely, and haven't hit the resolve yet which passes args to the callBulkDocLoad function, it is undefined.
Did I miss something?
Thanks.
It almost works.
The return did keep the loop finishing up the reduce loop before jumping out but it didn't do the resetImagesToPouch call until after all the loops were done and only once.
var argsSaved;
...
// WorkIssues
}).then(function () {
if (DB_WorkIssues != null)
return DB_WorkIssues.erase();
}).then(function () {
return DB_WorkIssues = new PouchDB(DBNAME_WorkIssues, { auto_compaction: true });
}).then(function () {
return ajaxCallForJsonTwoResults(URI_WorkIssues);
}).then(function (args) {
argsSaved = args;
return args[1].reduce(function (promise, image) {
return promise.then(function () {
return resetImagesToPouch(image);
});
}, Promise.resolve(args)).then(function () {
console.log('all done with images');
});
}).then(function (args) {
return callBulkDocLoad(DB_WorkIssues, argsSaved[0]);
}).then(function () {
updateWorkIssuesJson();
loadTaskList(false);
Here is the resetImagesToPouch function:
var resetImagesToPouch = function resetImagesToPouch(_ref) {
var image = _ref.AttachmentFile,
id = _ref.JobNumber,
imageName = _ref.DocTitle;
return DB_TaskImages.get(id, {
attachments: true
}).then(function (doc) {
return DB_TaskImages.putAttachment(id, imageName, doc._rev, image, 'image/jpeg');
}).catch(function (err) {
if (err.status = '404') {
// if this is a new document - add new attachment
//var imageType = GetImageTypeFromBase64(image);
return DB_TaskImages.putAttachment(id, imageName, image, 'image/jpeg');
} else {
throw err;
}
});
};
Before the return, the loop only happened once then jumped out. There are six images and the loop now happens times but doesn't do the resetImagesToPouch until all the loops are done.
So it goes like this: It does the reduce first, then it does "return promise.then" and Promise.Resolve six times, then it jumps to the "then" then it jumps back once to the resetImagesToPouch where it does the DB_TaskImages.get to see if this is the first image for this id and since the database is empty, it goes to the catch and inside the 404 if statment and then the DB_TaskImages.putAttachment where it where it jumps to the end of the function and then to the catch at the end of the chain with the error:
err = r {status: 404, name: "not_found", message: "missing", error: true, reason: "deleted"}
That was the error we got from the .get call that gave us the 404 (I believe), so I don't know why it went there. This doesn't seem to be the error you would get from a put. So it never does the last part of the chain, since it jumped over it and there was no image record put in the table.
Thanks.
After it was pointed out that I was missing a "==", I changed resetImagesToPouch() and fixed that as well as added thens and catches to the puts. This seemed to fix the problem of not finishing the chain.
var resetImagesToPouch = function resetImagesToPouch(_ref) {
var image = _ref.AttachmentFile,
id = _ref.JobNumber,
imageName = _ref.DocTitle;
return DB_TaskImages.get(id, {
attachments: true
}).then(function (doc) {
DB_TaskImages.putAttachment(id, imageName, doc._rev, image, 'image/jpeg').then(function () {
console.log("Success insert of multiple attachment");
return;
}).catch(function () {
console.log("Error in resetImagesToPouch: " + err);
showMsg("Error in resetImagesToPouch: " + err);
})
}).catch(function (err) {
if (err.status == '404') {
// if this is a new document - add new attachment
//var imageType = GetImageTypeFromBase64(image);
DB_TaskImages.putAttachment(id, imageName, image, 'image/jpeg').then(function () {
console.log("Successfull insert of multiple attachment");
return;
}).catch(function () {
console.log("Error in resetImagesToPouch: " + err);
showMsg("Error in resetImagesToPouch: " + err);
});
} else {
console.log("At throw error");
throw err;
}
});
};
But it still does not insert any images. The parameters look fine but it always takes the catch with the above 404 error with the "not found" and "deleted". The only thing I can think (but not sure why this would be the case) is that I do an erase on this database (as I do for all the databases) before I do these sections.
// TaskImages
}).then(function () {
if (DB_TaskImages != null)
return DB_TaskImages.erase();
}).then(function () {
return DB_TaskImages = new PouchDB(DBNAME_TaskImages, { auto_compaction: true });
I do this for about 6 databases because I am reloading them from the server. I wondered if maybe the .gets and .puts in the loop may be causing the issue? Doesn't seem to make much sense.
I just tried something new with the erase and changed it to destroy. This works better. Unlike the erase, which never adds any of the images, this one will add 4 of the 6 images. It gets a 404 error on two of them but instead of saying reason: "deleted", it says reason: "missing". I think the issue with this one is that these are actually multiple attachments for the same ID. But for some reason when the .get was done, it didn't find the record, so it did a .put without a revision (took the 404 route) and found the record at this point and gave the error because the revision was missing. Perhaps, when it first record it hadn't finished when the 2nd record came through.
Thanks.
I redid the resetImagesToPouch earlier today because I just couldn't get the old version to work. It always almost works but there was always something that didn't and I was running out of time. The 404 errors caused by the erase and the missing images were an issue.
I ended up going back to my resetImagesToPouch being a promise and was finally able to get it to work without jumping and for some reason had no issues with the images, as long as I did a destroy and not an erase. An erase would not save anything. It just came back with 404-missing or 404-deleted. I just ran out of time trying to figure out why.
Here is what I finally did this afternoon that worked all the way around.
// TaskImages
}).then(function () {
if (DB_TaskImages != null)
return DB_TaskImages.destroy();
}).then(function () {
return DB_TaskImages = new PouchDB(DBNAME_TaskImages, { auto_compaction: true });
// WorkIssues
}).then(function () {
if (DB_WorkIssues != null)
return DB_WorkIssues.erase();
}).then(function () {
return DB_WorkIssues = new PouchDB(DBNAME_WorkIssues, { auto_compaction: true });
}).then(function () {
return ajaxCallForJsonTwoResults(URI_WorkIssues);
}).then(function (args) {
argsSaved = args;
console.log("Before the reduce");
return args[1].reduce(function (promise, image) {
console.log("After the reduce and before the promise.then");
return promise.then(function () {
console.log("Inside the promise.then and before the resetImagesToPouch");
return resetImagesToPouch(image).then(function () {
console.log('Image written to Pouch successfully');
}).catch(function (err) {
console.log("Error in resetImagesToPouch with err: " + err);
});
console.log("After the resetImagesToPouch");
});
console.log("Before the Promise.resolve");
}, Promise.resolve(args)).then(function () {
console.log('all done with images');
});
}).then(function (args) {
console.log("Before the callBulkDocLoad");
if (argsSaved[2].length > 0) {
console.log("Ajax issue with err: " + argsSaved[2][0]);
msg = "Ajax issue with err: " + argsSaved[2][0];
}
return callBulkDocLoad(DB_WorkIssues, argsSaved[0]);
}).then(function () {
console.log("Before the updateWorkIssuesJson");
updateWorkIssuesJson();
loadTaskList(false);
And the resetImagesToPouch:
function resetImagesToPouch(_ref) {
var image = _ref.AttachmentFile,
id = _ref.JobNumber,
imageName = _ref.DocTitle;
return new Promise(function (resolve, reject) {
var rev;
var imageType;
// check if attachment already exists
DB_TaskImages.get(id, { attachments: true }).then(function (doc) {
rev = doc._rev; // Get the revision number since we are updating and adding attachment to existing doc.
DB_TaskImages.putAttachment(id, imageName, rev, image, "image/jpeg").then(function () {
console.log("Attachment added successfully");
return;
}).then(function () {
resolve();
}).catch(function (err) {
reject(err);
});
return;
}).catch(function (err) {
if (err.status = '404') { // if this is a new document - add new attachment
//imageType = GetImageTypeFromBase64(image);
DB_TaskImages.putAttachment(id, imageName, image, "image/jpeg").then(function () {
console.log("Attachment added successfully");
return;
}).then(function () {
resolve();
}).catch(function (err) {
reject(err);
})
}
});
}); // end promise
}
It may not be the best way to handle it but it is working. I do understand it better now but still need to work with it for the pieces I don't. Such as the part inside the reduce, I don't really understand all that yet and will look at it when I have more time. I do get some of it. But not sure why it bounces back and forth between the promise.then and the Promise.resolve for all the images before it does all the resetImagesToPouch calls. That will give me something to work on when I am out of town for Easter.
Thanks for all the help.
I doubt I would have got it working out without it.
Upvotes: 0
Views: 110
Reputation: 1
Using array.reduce is simple, once you have a valid array of course
let images = [
{ id: '111', imageName: 'fighter.jpg', image: 'aaaaaddddd' },
{ id: '111', imageName: 'horse.jpg', image: 'aaasdfssdaaddddd' },
{ id: '112', imageName: 'cat.jpg', image: 'aasdfaaadsdfsfdddd' },
{ id: '113', imageName: 'dog.jpg', image: 'aaasdfasfaaaddddd' },
{ id: '1234', imageName: 'apple.jpg', image: 'aaaasdfasaaddddd' }
]
rewrite the resetImagesToPouch
to correctly return a promise
let resetImagesToPouch = ({image, id, imageName}) =>
DB_TaskImages.get(id, {
attachments: true
})
.then((doc) => DB_TaskImages.putAttachment(id, imageName, doc._rev, image, 'image/jpeg'))
.catch ((err) => {
if (err.status = '404') { // if this is a new document - add new attachment
//var imageType = GetImageTypeFromBase64(image);
return DB_TaskImages.putAttachment(id, imageName, image, 'image/jpeg');
} else {
throw err;
}
});
Now, you let reduce do it's magic
images.reduce((promise, image) => promise.then(() => resetImagesToPouch(image)), Promise.resolve())
.then(() => {
console.log('all done');
});
if ES2015+ scares you
var resetImagesToPouch = function resetImagesToPouch(_ref) {
var image = _ref.image,
id = _ref.id,
imageName = _ref.imageName;
return DB_TaskImages.get(id, {
attachments: true
}).then(function (doc) {
return DB_TaskImages.putAttachment(id, imageName, doc._rev, image, 'image/jpeg');
}).catch(function (err) {
if (err.status = '404') {
// if this is a new document - add new attachment
//var imageType = GetImageTypeFromBase64(image);
return DB_TaskImages.putAttachment(id, imageName, image, 'image/jpeg');
} else {
throw err;
}
});
};
Now, you let reduce do it's magic
images.reduce(function (promise, image) {
return promise.then(function () {
return resetImagesToPouch(image);
});
}, Promise.resolve()).then(function () {
console.log('all done');
});
Looking at the "chain" in the edited question
}).then(function () {
return ajaxCallForJsonTwoResults(URI_WorkIssues);
}).then(function (args) {
// add return here
return args[1].reduce(function (promise, image) {
return promise.then(function () {
return resetImagesToPouch(image);
});
}, Promise.resolve(args))
.then(function () {
console.log('all done with images');
// pass on args down the chain
return args;
});
}).then(function (args) {
return callBulkDocLoad(DB_WorkIssues, args[0]);
}).then(function () {
and for the sake of completeness - in ES2015+ that would be
})
.then(() => ajaxCallForJsonTwoResults(URI_WorkIssues))
.then(([docs, images]) =>
images
.reduce((promise, image) =>
promise.then(() =>
resetImagesToPouch(image)),
Promise.resolve()
)
.then(() => docs)
)
.then(docs => callBulkDocLoad(DB_WorkIssues, docs))
.then(() => {
Upvotes: 1