Okay, so I'm trying to create a deep folder structure and I'm looping through duplicate folder names and creating promises like this:
var promises = [];
_.each(files, function (file) {
promises.push(localFolder
.createFolderAsync(folder1Name, Windows.Storage.NameCollisionOption.OpenIfExists)
.then(function (folder1) {
return folder1.createFolderAsync(folder2Name, Windows.Storage.CreationCollisionOption.OpenIfExists);
})
.then(function (folder2) {
return folder2.createFileAsync(fileName, Windows.Storage.NameCollisionOption.replaceExisting)
})
);
});
return WinJS.Promise.join(promises);
The problem is the duplicate folders, I thought OpenIfExists would just return the existing folders, but instead I end up with folders called "folder1Name (1)", "folder1Name (2)", etc. Using FailIfExists also is not failing, so I suspect there's something wrong with my promise chain.
Can anyone pinpoint what I'm doing wrong?
The second parameter of the first createFolderAsync call should be Windows.Storage.CreationCollisionOption.openIfExists instead of NameCollisionOption.
var promises = [];
_.each(files, function (file) {
promises.push(localFolder
.createFolderAsync(folder1Name, Windows.Storage.CreationCollisionOption.openIfExists)
.then(function (folder1) {
return folder1.createFolderAsync(folder2Name, Windows.Storage.CreationCollisionOption.openIfExists);
})
.then(function (folder2) {
return folder2.createFileAsync(fileName, Windows.Storage.NameCollisionOption.replaceExisting)
})
);
});
return WinJS.Promise.join(promises);
Related
I have an Set array which contains multiple Ids.I would like to loop through the Set and make the api calls in parallel for each id and get back the user object, add it to a map.How can i achieve it.
The value is Set
userIds :Set[2]
0:"1"
1:"2"
data() {
return {
userIds: new Set(),
};
},
const res = getUsers(userId)
hope it will resolve your issues. i did not test, just writing code here directly.
// set requests
let allRequests = []
//you can use other loop based on your decession
this.userIds.forEach(id => { allRequests.push(axios.get(`your_url\${id}`)) })
// you can use await it is based on you requirement
axios.all(allRequests).then(axios.spread((...responses) => {
//make your map here using responses
})).catch(errors => {
// react on errors.
})
you can check this reference
I am currently working with a API that does not return JSON. To get around this, I take the response and push it to a array ( while formatting it to remove any indentation and split each number in the response ). I then use this array of 183 numbers and run a for loop against an array with 183 characters to generate an object ( with custom key value pairs ) from the response.
Where things get confusing is when I start to use the data in my HTML. Usually you can just say <p>{data.overallRank}</p> but I am getting the error that the object is undefined. This makes sense because the data = {} was not created until the function ran.
After searching for a solution, I cam across svelte await blocks. You can read on them here and look at the tutorial : https://svelte.dev/tutorial/await-blocks
After trying to implement this feature, I have the following code.
let playerStats = []
let proxy = "https://cors-anywhere.herokuapp.com/"
let url = proxy + "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=Hess"
const data = {};
let promise = getPlayer();
async function getPlayer() {
return await fetch(url).then((response) => response.text())
.then((data) => {
return data;
});
}
getPlayer().then((playerData) => {
// format data
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
});
<main>
{#await promise}
<p>Search for a Player...</p>
{:then data}
<p>The data is {data}</p>
{/await}
</main>
I suggest throwing this code in a svelte editor which you can find here: https://svelte.dev/tutorial/await-blocks
The issue with this code is that it is printing out the data from the return data, which returns the unformatted data and not the object.
I want to return the object that is created after the second function getplayer().then()... so I can use that object throughout my HTML.
I hope I explained things well and thank you in advance for any help.
It is returning the formatted data because that what is returned by the promise function. In order to get the formatted data, you have to add the formatting to the chain of promise
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((playerData) => {
// here your transformation
// do not forget to actually return something
return data;
});
You were actually very close to sorting it out, just a bit of confusion regarding how promises work I believe.
All you need to do is format your data within the block where the data is handled following the fetch & decode operations:
async function getPlayer() {
return await fetch(url)
.then((response) => response.text())
.then((data) => {
return formatData(data);
});
}
Your formatData() function is essentially there already, you just need minor changes in your code:
function formatData(playerData) {
playerStats.push(playerData.replace(/\n/ig, ",").split(','));
console.log(playerStats);
// Begin object generation
// names array shortened
let names = ["overallRank", "overallLvl", "overallXP", "attRank", ]
const data = {};
for (var i = 0; i < playerStats[0].length; i++) {
data[names[i]] = playerStats[0][i];
}
console.log(data);
return data;
}
Finally, you do not need to explicitly declare a promise to use it in an {#await} block, you know getPlayer() returns a promise, so you can directly use that instead:
<main>
{#await getPlayer()}
<p>Search for a Player...</p>
{:then data}
<p>Overall Rank: {data.overallRank}</p>
{/await}
</main>
See functioning REPL
I have this method to get data from an API, which sends me information of many furniture pieces:
loadPieces() {
this.isLoading = true;
axios.get(this.galleryRoute)
.then(r => {
this.gallery = r.data;
this.isLoading = false;
})
.catch(error => {
this.$nextTick(() => this.loadPieces());
});
console.log(this.galleryRoute);
},
This is a part of the response I get, which represents only one piece:
[[{"id":266,"name":" Tray 7x45x32, white stained ash","thumbnail":{"width":840,"height":840,"urls":{"raw":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a.jpeg","small":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#140.jpeg","medium":"http:\/\/localhost:8888\/storage\/9c\/9d\/9c9dadc6-15a2-11e8-a80a-5eaddf2d1b4a#420.jpeg"}}},
Now I want to create a filter so that I can get a specific piece from the JSON object, using it's id. I've tried searching but so far I have no idea how to do this.
Thanks in advance!
Add a computed property which applies the filter to this.gallery:
computed: {
filteredGallery() {
if (!this.gallery) return []; // handle gallery being unset in whatever way
return this.gallery.filter(picture =>
// some reason to show picture
);
}
}
I'm assuming gallery is an array, but you could apply a similar technique to it if it was an object, using e.g. Object.keys(this.gallery).
Then in your template, use filteredGallery instead of gallery.
It seems in sailsjs you can only run and pass one set of query data at a time. For example here is the controller for my homepage:
module.exports = {
index: function (req, res) {
Blog.find()
.limit(3)
.sort('createdAt desc')
.where({ isPublished: 1 })
.exec(function(err, posts) {
if (err) return next(err);
res.view({
layout: "homeLayout",
posts:posts
});
});
}
};
How would I query data from some other model and pass it to my view along with the blog data Im already passing?
You can use Promises to do so. It's actually an excellent usecase.
I use Q, which is what Waterline (Sail's ORM) use behind the scene.
You can see below an example of code where I retrieve data from a first model, and then, using the data I retrieved, I query other models to get some more data (in parallel), and in the end, I send the result back to the view.
SomeModel.findOne(criterias).then(function(result) {
Q.all([
SomeOtherModel.getSomething(result),
YetAnotherModel.getSomethingElse(result)
]).spread(function(someOtherResult, yetAnotherResult) {
var data = {
thing: result,
stuff: someOtherResult,
otherthing: yetAnotherResult
};
return res.view(data);
});
}).fail(function(reason) {
return res.view(reason);
});
The getSomething() function should return a promise, standard finder from Sails will work transparently (just don't pass the callback). As per this other question it appears that standard finder do not behave exactly like Q promises, the answer I gave there should help get a more consistant behavior.
More on Q and how it works in the doc !
You could also use async.auto (see below). Here's a link to the complete sails repo example.
var async = require('async'),
_ = require('lodash');
module.exports = {
index: function (req, res) {
async.auto({
// Get the blog posts
posts: function (cb) {
Blog.find()
.where({ isPublished: 1 })
.limit(5)
.sort('createdAt DESC')
.exec(cb);
},
// Get some more stuff
// (this will happen AT THE SAME TIME as `posts` above)
otherThings: function (cb) {
OtherThing.find()
.limit(30)
.exec(cb);
},
// Get comments
// (we'll wait until `posts` is finished first)
comments: ['posts', function (cb, async_data) {
// Get `posts`
// (the second argument to cb() back in `posts`)
// Used map to make sure posts are an array of ids and not just an object.
var posts = async_data.posts.map(function (item){ return item.id});
// Get comments that whose `post_id` is equal to
// the id of one of the posts we found earlier
Comment.find()
.where({ post_id: posts })
.exec(cb);
}]
},
function allDone (err, async_data) {
// If an error is passed as the first argument to cb
// in any of the functions above, then the async block
// will break, and this function will be called.
if (err) return res.serverError(err);
var posts = async_data.posts;
var comments = async_data.comments;
var otherThings = async_data.otherThings;
// Fold the comments into the appropriate post
// An in-memory join
_.map(posts, function (post) {
var theseComments =
_.where(comments, { post_id: post.id });
post.comments = theseComments;
});
// Show a view using our data
res.json({
// layout: 'homeLayout',
posts: posts,
otherThings: otherThings
});
});
}
};
I have figured out a few ways to accomplish this. The first way is to nest your queries, eg.
Blog.find()
.limit(30)
.sort('createdAt desc')
.where({ isPublished: 1 })
.exec(function(err, posts) {
SomeOtherModel.find()
.limit(5)
.sort('createdAt desc')
.where({ isPublished: 1 })
.exec(function(err, otherdata) {
res.view({
posts: posts,
otherdata: otherdata
});
});
});
The second way is to use promises (I wasnt aware of this previously)
User.findOne()
.where({ id: 2 })
.then(function(user){
var comments = Comment.find({userId: user.id}).then(function(comments){
return comments;
});
return [user.id, user.friendsList, comments];
}).spread(function(userId, friendsList, comments){
// Promises are awesome!
}).fail(function(err){
// An error occured
})
The third way (I ended up going with this) is to create a policy (specific to sailsjs but is express middleware)
// saved as /api/policies/recentPosts.js
// also need to add a rule to /config/policies.js
module.exports = function (req, res, ok) {
Blog.find()
.limit(3)
.sort('createdAt desc')
.where({ isPublished: 1 })
.exec(function(err, footerposts) {
res.footerposts = footerposts;
return ok();
});
};
Doing it this way you dont need to pass anything to your view however Im not sure if its good practice to randomly add data to the response object.
So here is how you can make 3 requests and pass all their data into your view:
first install Q
npm install q
Then use code below and substitute my models with yours:
// first import Q
var Q = require('q');
// Let's combine results of 3 queries
Q.all([
// let's find one user with name "Pavel"
User.findOne({name: 'Pavel'}).then(),
// let's find one Lexus car
Cars.findOne({brand: 'Lexus'}).then(),
// Finally let's get the first Apple phone
Phones.findOne({brand: 'Apple'}).then()
])
.spread(function (user, car, phone) {
// Output results as json, but you can do whatever you want here
res.json([user, car, phone]);
}).fail(function (reason) {
// output reason of failure
res.json(reason);
});
I currently have an issue with a file read in a Windows 8/WinRT application. I have a simple navigation style app, several pages have access to the same data and I have a data.js file that defines a namespace (Data) with a number of members. One part of the application saves items to a txt file stored in the applications local data folder. But on some of the other pages I need to read this in or check for the existence of an item within the list of previously saved items. To do this I added another method into the data.js file. The trouble is, when I call this method to check for the existence of an item, it doesn't return the value straight away due to the async nature, but the rest of code in the page specific js file still seems to execute before it jumps back into the parsing. This means that the logic to check for an item doesn't seem to work. I have a feeling it's down to my use of either .done or .then but my code is as follows:
DATA.JS
var doesItemExist= function(item_id){
var appFolder = Windows.Storage.ApplicationData.current.localFolder;
//note I've tried this with and without the first "return" statement
return appFolder.getFileAsync(dataFile).then(function (file) {
Windows.Storage.FileIO.readTextAsync(file).done(function (text) {
try {
var json = JSON.parse(text);
if (json) {
for (var i = 0; i < json.items.length; i++) {
var temp_item = json.items[i];
if (temp_item.id === item_id) {
return true;
break;
}
}
} else {
return false;
}
} catch (e) {
return false;
console.log(e);
}
}, function (e) { return false;console.log(e); });
}, function (e) { // error handling
return false;
console.log(e);
});
}
WinJS.Namespace.define("Data", {
doesItemExist: doesItemExist
}); //all of the above is wrapped in a self executing function
Then on Page.js I have the following:
var add = document.getElementById('add');
if (Data.doesItemExist(selected_item.id)) {
add.style.display = 'block';
} else {
add.style.display = 'none';
}
All the variables here are assigned and debugging doesn't produce any errors, control just appears to go back to the if/else statement after it hits the getFileAsync but before it even goes through the for loop. But subsequently it does go in to the for loop but after the if statement has finished. I'm guessing this is down to the async nature of it all, but I'm not sure how to get around it. Any ideas?
thanks
A Promise should work here.
I created a new Navigation app, and added a Data.js file containing the following code:
(function () {
var appData = Windows.Storage.ApplicationData;
function doesItemExist(item_id) {
return new WinJS.Promise(
function (completed, error, progress) {
var exists = false;
appData.current.localFolder.createFileAsync("data.txt", Windows.Storage.CreationCollisionOption.openIfExists).then(
function (file) {
Windows.Storage.FileIO.readTextAsync(file).then(
function (fileContents) {
if (fileContents) {
if (fileContents = "foo!") {
completed(true);
}
else {
completed(false);
}
}
else {
completed(false);
}
}
);
},
function (e) {
error(e);
}
);
}
);
}
WinJS.Namespace.define("Data", {
doesItemExist: doesItemExist
});
})();
Note that I've simplified the code for retrieving and parsing the file, since that's not really relevant to the problem. The important part is that once you've determined whether the item exists, you call completed(exists) which triggers the .then or .done of the Promise you're returning. Note that you'd call error(e) if an exception occurs, as I'm doing if there's an exception from the call to createFileAsync (I use this call rather than getFileAsync when I want to be able to either create a file if it does not exist, or return the existing file if it does, using the openIfExists option).
Then, in Home.js, I added the following code to the ready handler:
var itemExists;
var itemExistsPromise = Data.doesItemExist(42);
itemExistsPromise = itemExistsPromise.then(function (exists) {
itemExists = exists;
var content = document.getElementById("content");
content.innerText = "ItemExists is " + itemExists;
});
itemExistsPromise.done(function () {
var a = 42;
});
var b = 0;
The code above sets the variable itemExistsPromise to the returned promise from the function in Data.js, and then uses an anonymous function in the .then function of the Promise to set the variable itemExists to the Boolean value returned from the doesItemExist Promise, and grabs the <p> tag from Home.html (I added an id so I could get to it from code) and sets its text to indicate whether the item exists or not). Because I'm calling .then rather than .done, the call returns another promise, which is passed into the itemExistsPromise variable.
Next, I call itemExistsPromise.done to do any work that has to wait until after the work performed in the .then above it.
If you set a breakpoint on the lines "var a = 42" and "var b = 0" (only included for the purpose of setting breakpoints) as well as on the line "itemExists = exists", you should find that this gives you the control you need over when the various parts are executed.
Hope that helps!