Promises in series - javascript

I have problem running multiple premises in series, using Q and reduce idiom (see: http://taoofcode.net/promise-anti-patterns/)
I tried to follow this example, change it a little bit and here's what I've got:
playlist.createPlaylist('playlist')
.then(function(playlistId) {
songsInfo.reduce(function (accumulatedPremise, songInfo) {
accumulatedPremise.then(function (response) {
var def = Q.defer();
youtube.search(songInfo, function (songInfo, resp) {
var song = songParser.parse(songInfo, resp);
if (song !== null) {
playlist.addSongToPlaylist(song, playlistId).then(function(response) {
def.resolve(response)
})
}
});
return def.promise;
})}, Q());
});
Now, I should probably talk a little about main idea behind this:
1) I'm creating a youtube playlist. From createPlaylist function, I'm returning a promise with playlist's ID and then
2) for each SongInfo I have I need to invoke search method on my youtube module. Search function also takes a callback. In this case, I'm invoking addSongToPlaylistMethod if desired song was found/parsed correctly. As you can see, addSongToPlaylist also returns a Promise.
Now, the problem:
all the block inside
then
method, invoked on
accumulatedPremise
is executed only once.
You could probably ask me why simply not to use forEach in this case? Well, because of
http://www.acnenomor.com/3037813p1/playlistitems-batch-insert-youtube-api-v3
Could you please help me fixing it?
Cheers,
Slawek

You're not returning anything from the reduce function.
accumulatedPremise.then(function (response) {
should be:
return accumulatedPremise.then(function (response) {

Related

promisifyAll in bluebird.js

I am trying to understand how promisifyAll works. i am trying hard to understand the example given in their documentation. Can anyone please give me a simple example, how it works? I am trying to understand the concept and how we can use it. Thank you
var objects = {};
objects.first = function(){
console.log("First one is executed");
}
objects.second = function(){
console.log("second one is executed");
}
objects.third = function(){
console.log("third one is executed");
}
how can we promisify the objects variable? or something like that.
First, you need to understand how a nodeFunction works. It takes a callback function as the last argument. This callback takes two arguments, the first one is error and the second the data. Consider for example require("fs").readFile:
// This is the callback
function callback (error, data) {
if (error) {
console.error('There was an error', error)
} else {
console.log('This is the data', data)
}
}
require('fs').readFile('my-file.txt', callback)
Note that this is a convention, there's nothing in JS itself that enforces it.
Let's move to Promise.promisify now. This method takes a nodeFunction and returns a promisified version of it. This is more or less what it does:
function promisifiedReadFile (filePath) {
return new Promise(function (fulfill, reject) {
require('fs').readFile(path, function (error, data) {
if (error) { reject(error) } else { fulfill(data) }
})
})
}
It's a bit verbose but as you can see you now have a version of readFile that returns a promise instead of accepting a callback. Of course, this example is hard-coded for readFile, Promise.promisify works for any nodeFunction instead:
const promisifiedReadFile = Promise.promisify(require('fs').readFile)
The two promisifiedReadFile methods work in the same way.
Last, Promise.promisifyAll takes an object, goes through it and finds all the methods, it then applies Promise.promisify to each of them.
So, if you call Promise.promisifyAll(require('fs')) you get back a version of the fs module where all the methods return promises instead of accepting callbacks.
About your example I'm not sure what you're trying to achieve but the methods you defined are not nodeFunctions so they can't be promisified.

Return result aSync function in method

First of all, I completely read all the answers on this question, but despite that I come to realize that after years of scripting I ended up in the aSync hell.
I have a method that uses an async function. Depending on the result of that function, the method should return true or false.
So, simply said:
example = {
overview: undefined,
aSyncFunction: function (callback) {
// Adds values to overview, which we will use in otherFunction
callback();
return this;
},
otherFunction: function (data) {
var result = false;
this.aSyncFunction( function () {
var available = this.overview[data.name];
// result == filter overview with supplied data)
}.bind(this));
return result;
}
};
I've created a JsFiddle to show you the exact situation I'm in: https://jsfiddle.net/copt436a/1/
Deleting the setTimeOut will deliver true, otherwise it is false.
Note, I'm cannot change aSyncFunction at the moment, so the solution must be somewhere in otherFunction
I tried to separate the callback function in a different function, but in that case the return value is stuck in that particular function otherFunction keeps returning undefined. Also using the return value of aSyncFunction does not give me the result I want, cause this returns this.
I'm completely stuck on this one, and probably the solution is quite simple.

Most efficient way to receive json async responses?

(my case applies to C#, MVC, returning JSON, got jquery, angular), but I expect it applies to more than that.
I have a website where my angular/html/js calls ~7 services through Angular controllers and async-gets/displays data (weather, road conditions, etc). Some of these take longer than others (from ms to ~10s). I'd like to have a single call to my service which returns all of this data - but doesn't wait until the last call to return anything (10s).
Is there a way to make a single call, and return results as I have them and they get displayed accordingly? Do I need to have a repeating call which has a boolean like "IsMore=T" and calls the service again? (doesn't sound efficient).
Ideally, I'd like to keep a response channel open and keeping pumping results until it's done. Possible?
I'm not sure I understand completely, but I think you could just chain the response promises together, something like:
$scope.getWeatherData = function () {
return myWeatherService.get().then(function (resp) {
$scope.weatherData = resp;
});
}
$scope.getTrafficData = function () {
return myTrafficService.get().then(function (resp) {
$scope.trafficData = resp;
});
}
$scope.getWeatherData.then(getTrafficData).then(...chain others...);
This assumes that the service calls return a $http promise.
Anyway, whenever the promise comes in, the data will be on $scope, and hence the display will be updating as the promises arrive. It sounds like you might be using something like $q.all(), which would wait until all promises are resolved.
Buildilng on #reptilicus' answer:
$scope.getWeatherData = function () {
return myWeatherService.get().then(function (resp) {
$scope.weatherData = resp;
});
};
$scope.getTrafficData = function () {
return myTrafficService.get().then(function (resp) {
$scope.trafficData = resp;
});
};
$q.all([
$scope.getWeatherData(),
$scope.getTrafficData()
]).then(function () {
// do whatever's next, or nothing
});
... would request/receive both responses in parallel (if that's what you want). The relevant $scope property for each request will be populated when the related response is received, and the "do whatever's next" code will run once they are all complete.
Note, you need to inject $q to your controller constructor for this to work. :)
Edit: I just noticed that #reptilicus did mention $q.all. The difference between chaining the .thens and $q.all is that under chaining, one request wouldn't start until the previous was received....

Mutating arguments in jQuery promises

tl;dr : I'm looking for a way to have the first .then callback make changes to the data that is passed to subsequent chained events.
I have a library that encapsulates some async operations.
dudetools.getDude(2); // causes an XHR against REST resource "Dude" for row id 2
For awesomeness purposes, dudetools.getDude returns the promise created by the underlying $.ajax call. Thus, I can do things like:
dudetools.getDude(dudeId).done(function(dudeData) { /* do stuff with dude's data */ });
Now I'm trying to modify dudetools so that it'll do some convenient data-massaging on response data before continuing along the promise chain. I want this massage to happen universally, without calling code having to request it or even know about it.
Because the dudetools implementation can't share a closure with all calling code, I'm hoping to leverage the fact that, in JavaScript, non-scalars are always passed by reference rather than by value.
Consider:
var urStuff = {};
function wreck(x) {
x.isWrecked = 'so wrecked';
}
wreck(urStuff);
// urStuff.isWrecked === 'so wrecked' ^.^
I dare you to try it.
So, I was hoping this would work:
dudetools = {
'getDude': function(dudeId) {
return $.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
// I'm so clever!
dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
});
}
}
Of course, it doesn't work. My clever code is being executed (I've seen it), and it's reaching the right conclusions, but subsequent Deferred events in the chain never see the modifications. I.e.:
$.when(
dudetools.getDude(dudeId)
).done(function(mysteriouslyUnmodifiedInfo) {
/* the info passed to this function is mysteriously unmodified! HALP */
});
Any suggestions? Is there a way to accomplish what I'm after?
Also: I'm still kind of new to promises in general, and my grasp of the differences between Deferreds, Promises, and associated constructs is still kind of fuzzy, so I'd very much appreciate your efforts to be clear and explicit when explaining to me how I've ruined everything.
Thanks very much.
EDIT: updated to reflect fact that dudetools.getDude returns a promise, not a Deferred. Because I now (mostly) understand the difference.
The magic of .then is that it pipes its return value into the next callbacks param.
If you don't return your object (even if you haven't changed anything), then undefined is returned by default.
do_something()
.then(function (json) { return JSON.parse(json); })
.then(function (response) { return response.data; })
.then(function (data) { data.tweaked = true; return data; });
You'll want to return your own new Deferred.promise() object.
http://api.jquery.com/deferred.promise/
dudetools = {
'getDude': function(dudeId) {
var dfd = new jQuery.Deferred();
$.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
// I'm so clever!
dfd.resolve(dudeData);
});
return dfd.promise();
}
}
Hope that helps.

Chaining Promises recursively

I'm working on a simple Windows 8 app in which I need to fetch a set of data from a web site. I am using WinJS.xhr() to retrieve this data, which returns a Promise. I then pass a callback into this Promise's .then() method, which supplies my callback with the returned value from the asynchronous call. The .then() method returns another Promise, giving it the value that my callback returns. The basic structure of such a query would be as follows:
WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
//do stuff
return some_value;
}).then(
function secondcallback( some_value )
{
//do stuff
});
In my situation, however, I may need to make additional queries for data depending on the data returned by the first query, and possibly more queries depending on THAT data... and so on, recursively.
I need a way to code this such that the final .then() is not executed until ALL the recursions have completed, similar to this:
function recurse() {
return WinJS.xhr({ url: "http://www.example.com/" }).then(
function callback( result_from_xhr )
{
if( result_from_xhr == something )
{
recurse();
}
});
}
recurse().then(
function final()
{
//finishing code
});
The problem is that, of course, the finishing code is called as soon as the first level of recursion completes. I need some way to nest the new promise and the old promise from within the callback.
I hope my question is clear enough, I'm really not sure how to explain it and frankly the idea of asynchronous recursive code makes my head hurt.
What I would do here is to create a whole new, standalone promise that you can complete manually, and return that from the recurse() function. Then, when you hit the point that you know you're done doing async work, complete that promise.
Promise.join works when you've got a known set of promises - you need the entire array of promises available before you call join. If I followed the original question, you have a variable number of promises, with more possibly popping up as part of async work. Join isn't the right tool in these circumstances.
So, what does this look like? Something like this:
function doSomethingAsync() {
return new WinJS.Promise(function (resolve, reject) {
function recurse() {
WinJS.xhr({ url: "http://www.example.com/" })
.then(function onResult(result_from_xhr) {
if (result_from_xhr === something) {
recurse();
} else {
// Done with processing, trigger the final promise
resolve(whateverValue);
},
function onError(err) {
// Fail everything if one of the requests fails, may not be
// the right thing depending on your requirements
reject(err);
});
}
// Kick off the async work
recurse();
});
}
doSomethingAsync().then(
function final()
{
//finishing code
});
I rearranged where the recursion is happening so that we aren't recreating a new promise every time, thus the nested recurse() function instead of having it at the outer level.
I solved this problem in perhaps a different way (I think it's the same problem) while making my own Windows 8 app.
The reason I came across this problem is because, by default, a query to a table will paginate, and only return 50 results at a time, so I created a pattern to get the first 50, and then the next 50, etc, until a response comes back with less than 50 results, and then resolve the promise.
This code isn't going to be real code, just for illustration:
function getAllRows() {
return new WinJS.Promise(function(resolve, reject){
var rows = [];
var recursivelyGetRows = function(skipRows) {
table.skip(skipRows).read()
.then(function(results){
rows = rows.concat(results);
if (results.length < 50) {
resolve(rows);
} else {
recursivelyGetRows(skipRows + 50);
}
})
}
recursivelyGetRows(0);
});
}
so getAllRows() returns a promise that resolves only after we get a result back with less than 50 results (which indicates it's the last page).
Depending on your use case, you'll probably want to throw an error handler in there too.
In case it's unclear, 'table' is a Mobile Services table.
You will need to use Promise.join().done() pattern. Pass an array of Promises to join(), which in your case would be a collection of xhr calls. The join() will only call done() when all of the xhr Promises have completed. You will get an array of results passed to the done(), which you can then iterate over and start again with a new Promise.join().done() call. The thing to be away of, when using this approach, is that if one of the Promises passed to join() fail, the entire operation is treated as an error condition.
Sorry I don't have time right now to try and stub out the code for you. If I get a chance, I will try to later. But you should be able to insert this into your recursive function and get things to work.
Well, I solved my problem; my recursive function was misinterpreting the data and thus never stopped recursing. Thank you for your help, and I'll be sure to watch those screencasts, as I still don't quite fully grasp the Promise chaining structure.

Categories