How to use Bluebird promisification with generators + parallel promises - arrays

Trying to fire off mutiple requests off to the beats api using bluebird as well as koa for generators.
After reading some documentation I figured the following would work
var request = require('co-request'),
_ = require('lodash'),
Promise = require('bluebird');
request = Promise.promisifyAll(request);
module.exports.getTracks = function *tracks(){
firstCall = yield makeAPICall('users/' + me + '/mymusic/tracks?limit=150');
total = firstCall.body.info.total;
total -= 150;
tracks = firstCall.body.data;
//Beats only allows a maximum of 150 tracks per call
//If more tracks are needed then the remainder is called in sets of 150
var offset = 150;
while (total > 0) {
promises.push(makeAPICall('users/' + me + '/mymusic/tracks?limit=150&offset=' + offset));
offset += 150;
total -= 150;
}
var responses = yield(Promise.all(promises));
}
function makeAPICall (query){
var authOptions = {
url: 'https://partner.api.beatsmusic.com/v1/api/' + query,
headers: { 'Authorization': 'Bearer ' + accessToken },
json: true
};
return request.get(authOptions);
}
The method makeAPI call works as expected used with firstCall, but for some reason when I start placing the makeAPICall method into the array they never seem to execute. The variable responses yields out just an array of functions instead of an array of responses from the beats api. What do I need to change to make responses return an array of objects similar to that of firstCall?

Your using co-request which already converts callbacks to thunks, so there is no need to try and promisify things.
Here is a simplified runnable example, similar to your code, showing how to run api calls in parallel with Koa (which uses co under the hood).
When you yield an array, co will run any thunks/promises/generators etc in parallel.
var request = require('co-request'),
co = require('co');
co(function *(){
var results = yield getTracks();
results.forEach(function(result){
console.log(result.body);
})
}).then();
function * getTracks () {
var queries = [];
// swap out your queries here
queries.push(makeAPICall('5185415ba171ea3a00704eed'));
queries.push(makeAPICall('54fdc3c9862a3aab01dc95cf'));
queries.push(makeAPICall('54fdc3da862a3aa501dc95d0'));
// yielding an array returns an array of results
var results = yield queries;
return results;
}
function makeAPICall (query) {
var options = {
url: 'http://www.mocky.io/v2/' + query,
json: true
}
return request.get(options)
}

Related

merge array of objects from fetched API with pagination with Google Apps Script

I'm fetching a URL. The full response is spread over five pages.
I'm looping through each pages which returns me an array of object (please correct me if I'm wrong):
[{item_1=foo, item_2=bar, item_3=foobar, value_1=XX}, {item_1=bar, item_2=foo, item_3=barfoo, value_1=XX},etc...]
I want to consolidate all the response like if it was one big array of objects.
So far, I wrote this:
for (i = 1; i <= total_pages; i++) {
var rawResponse = UrlFetchApp.fetch(
'url',
{
method: 'GET'
})
response[i] = JSON.parse(rawResponse);
}
var g = response[1].concat(response[2], response[3],response[4],response[5]);
g contains the desired output; however, as you can see, this is not dynamic. How can I solve this? I could you the push method, but I would return me a new array with each response.
In order to make your code "dynamic" you could use the concat function inside the for-loop, for each of the pages. A possible modification of your code could look like the following, where the result variable would contain all the results:
var result = [];
for (var i = 1; i <= total_pages; i++) {
var rawResponse = UrlFetchApp.fetch(
'url',
{
method: 'GET'
}
);
var current = JSON.parse(rawResponse);
result = result.concat(current);
}

Is it normal than my $http request get resolved in inverse order? (angularjs)

I have a function inside a service that makes $http.get request from an array
var ids = [1,2,3]
var extensionPromises = [];
for (var i = 0 ; i < extensions.length ; i++) {
var myPromise = myHttpService.getOneExtension(ids[i]);
extensionPromises.push(myPromise);
}
return $q.when(
Promise.all(extensionPromises).then(function(all){
$rootScope.$broadcast("allReady");
return true;
}).catch(function(error){
var e = {};
e.error = error;
e.error.type = "http.get";
return (e);
})
);
That send the ID to a simple $http.get( myUrl + id).then()..., all work ok but when I see the XHR get info in the console, it resolves in the inverse order, that is 3, 2, 1.
Is this normal?
There are no guarantees on order of resolve of multiple async requests. They are subject to network time, server time etc. Even 2 requests to same endpoint may not return in same order they were sent

Prevent multiple submits in angularjs

I'm looking for a AngularJS-based way to prevent multiple submits per task.
I don't need buttons to be disabled after submission or close the form and wait for the task to be completed. Instead, I need requests to be unique.
To be more detailed, I need $http.get and $http.post stop sending multiple same requests.
Any Ideas?
According to this article, you can use provider decorator.
NOTE: this approach is based on angular-api
https://gist.github.com/adambuczynski/354364e2a58786e2be71
UPDATE
I've changed a little part in your suggested solution, because returned promises have lost .success and .error and .then.
Just use this edited code to have all of those functions working:
.config(["$provide", function ($provide) {
$provide.decorator('$http', function ($delegate, $q) {
var pendingRequests = {};
var $http = $delegate;
function hash(str) {
var h = 0;
var strlen = str.length;
if (strlen === 0) {
return h;
}
for (var i = 0, n; i < strlen; ++i) {
n = str.charCodeAt(i);
h = ((h << 5) - h) + n;
h = h & h;
}
return h >>> 0;
}
function getRequestIdentifier(config) {
var str = config.method + config.url;
if (config.data && typeof config.data === 'object') {
str += angular.toJson(config.data);
}
return hash(str);
}
var $duplicateRequestsFilter = function (config) {
if (config.ignoreDuplicateRequest) {
return $http(config);
}
var identifier = getRequestIdentifier(config);
if (pendingRequests[identifier]) {
if (config.rejectDuplicateRequest) {
return $q.reject({
data: '',
headers: {},
status: config.rejectDuplicateStatusCode || 400,
config: config
});
}
return pendingRequests[identifier];
}
pendingRequests[identifier] = $http(config);
$http(config).finally(function () {
delete pendingRequests[identifier];
});
return pendingRequests[identifier];
};
Object.keys($http).filter(function (key) {
return (typeof $http[key] === 'function');
}).forEach(function (key) {
$duplicateRequestsFilter[key] = $http[key];
});
return $duplicateRequestsFilter;
})
}])
It could be a performance issue but following idea could solve your problem.
Store the each request URL and DATA as key value pair on a variable. URL should be KEY. For Same URL multiple submission can be stored in a Array.
Then for any new call check the URL if it present in your stored object, then compare the data with each object thorughly (deep check, it is costly though).
If any exact match found then stop the processing. As same request came.
Other wise proceed and don't forget to store this data also.
But it is costly since need to check the data which could be havy.
Note: At the time of storing the data you could convert it to JSON String so it will be easier to compare between String.
here is the Code Algo
YourService.call(url, params) {
var Str1 = JSON.stringify(params);
if(StoredObj[url]) {
for each (StoredObj[url] as Str){
if(Str === Str1) {
return;
}
}
}
else {
StoredObj[url] = []; //new Array
}
StoredObj[url].push(Str1);
Call $http then;
}

synchronous for loop with promises

I have the following function that works but not as I want it to since each iteration needs the results of the iteration before.
There are plenty of similar questions but I'm finding it hard to reduce the solutions down to a pattern.
How can I rewrite the following function so that each loop iteration "waits" for the one before it?
$scope.updateBarcode = function() {
var itemcodes = $scope.itemcode.split(';');
// doc is JSPDF document, fetch will add a new page to the supplied doc.
var doc;
//fetch will unshift the returned doc onto the supplied array.
var result = [];
for (var i = 0; i < itemcodes.length; i++) {
var promise = $scope.fetch(doc, itemcodes[i],result);
promise.then(function(){
doc = result[0];
if (i >= itemcodes.length) {
doc.save(itemcodes[0] + '.pdf');
};
})
};
}
You need to assign the new promise from then() to your variable so that you build up a chain:
promise = promise.then(...);

Implementing pagination in backbone

I'm trying to implement a pretty basic pagination & filtering support with backbone
so far now, I have something like this on my Collection.url() function:
url: function() {
var url = 'http://localhost:9000/wines';
var query = '';
if (this.filter) query += '&filter=' + this.filter;
if (this.order) query += '&order=' + this.order;
if (this.page) query += '&page=' + this.page.toString();
if (this.len) query += '&len=' + this.len.toString();
if (query) url += '?' + query.substring(1);
return url;
},
you get the idea
the problem is that when I'm trying to create (POST) or update (PUT) an item, the querystring is also sent to the browser...
is there some way, in the url function, to detect the operation I'm issuing and add the querystring parameters only when fetching (GET) data?
or would you recommend another approach???
I found the following way to do it
I just override the fetch method, like this:
initialize: function(options) {
this._fetch = this.fetch;
this.fetch = function() {
options = {};
options.url = 'http://localhost:9000/wines'
data = {};
if (this.filter) data.filter = this.filter;
if (this.order) data.order = this.order;
if (this.page) data.page = this.page;
if (this.len) data.len = this.len;
options.data = data;
return this._fetch(options);
}
},
I couln't find a way to avoid saving the _fetch variable, I tried with
return Backbone.Collection.fetch(options);
return Backbone.Collection.prototype.fetch(options);
return Wines.fetch(options);
return Wines.prototype.fetch(options);
but none of them seems to work...
--
edit
found it on backbone page:
return Backbone.Collection.prototype.fetch.call(this, options);

Resources