I'm trying to take an async function, wrap it and call it:
s3 = new AWS.S3();
Meteor.methods({
getbuckets: function( params, buckets ) {
var buckets = Meteor.wrapAsync(
s3.listBuckets( params, function( err, data ) {
if ( err ) {
console.log( err, err.stack ); // error
} else {
return buckets;
}
return {};
}) // End listBuckets
); // End wrapAsync
return buckets( params );
}
});
When calling the method, I'm getting this error:
TypeError: Object # has no method 'apply'
this is pointing to the line with buckets( params )
Meteor.wrapAsync takes an existing asynchronous method method, such as s3.listBuckets. The second parameter contains the context this should bind to. This should work:
Meteor.methods({
getbuckets: function(params) {
var listBuckets = Meteor.wrapAsync(s3.listBuckets, s3)
return listBuckets(params);
}
});
Meteor.wrapAsync takes a function as an argument. What you are doing, is executing the function s3.listBuckets and passing the return value as an argument to Meteor.wrapAsync. Which doesn't have the prototype Function. So what you need to do is wrap it in an anonymous function, like this:
Meteor.methods({
getbuckets: function( params, buckets ) {
var buckets = Meteor.wrapAsync(
function bucketLister (params, resolveFuture) {
s3.listBuckets( params, function( err, data ) {
if ( err ) {
console.log( err, err.stack ); // error
resolveFuture(false)
} else {
resolveFuture(buckets);
}
return {};
}) // End listBuckets
} // End bucketLister
); // End wrapAsync
return buckets( params );
}
});
The function passed to Meteor.wrapAsync takes a function resolveFuture as it's argument. We pass the actual return value to it. To learn more about this check the meteor source code or the fibers documentation on resolvers
Related
I hope you are safe and doing well;
I've been working with SignalR and trying to create a better approach to handle when it is disconnected.
So, I create this piece of code to override the invoke and then add the request on an array (if disconnected) to execute after;;
const tobeExecuted = [];
myHub.start().then(() => {
while (tobeExecuted.length > 0) {
const delayed = tobeExecuted.pop()
delayed()
}
})
myHub.invoke = (function (_super) {
if (appHub.connectionState === 'Connecting') {
return function () {
tobeExecuted.push(() => {
_super.apply(this, arguments)
})
}
}
return function () {
return _super.apply(this, arguments)
}
}(myHub.invoke))
It works, however when I tried to use .then like
myHub.invoke("whenever").then(a => console.log(a))
.then is not defined. I know it is a Promisse, but don't know how to return properly
Can someone help?
I am experiencing an unexpected result :"should get data and has multiple recordSets" test case. My intention is to have the "stub.getRefundItems" method call return one set of results on the first call and a second set of results on the second call. The test works as expected if there are no other tests that use that method. However when I add in the additional test "should get data and has 1 recordSet" I get a single result (LineItemsHasNextFalse) no matter what I do. I should also mention the stub gets injected via awilix as a singleton. Am I wiring the stub up incorrectly? Is it not possible to change the results using onCall after you have set the returns value?
let stub = {
getRefund: sandbox.stub(),
getRefunds: sandbox.stub(),
getRefundItems: sandbox.stub(),
getRefundTransactions: sandbox.stub()
};
var sandbox = require('sinon').createSandbox();
describe('#refunds', function() {
beforeEach(function () {
sandbox.spy(logger)
})
afterEach(function () {
// completely restore all fakes created through the sandbox
sandbox.restore();
})
context('getRefundItems', function() {
it('should get data and has 1 recordSet', test(async function() {
stub.getRefundItems.returns(LineItemsHasNextFalse)
let resp = await refunds.getRefundItems({"gid":"shjdjs"})
expect(resp).to.deep.equal(LineItemsHasNextFalse.data.refund.refundLineItems.edges)
}))
it('should get data and has multiple recordSets', test(async function() {
let correctResp = new Array()
correctResp.push(...LineItemsHasNextTrue.data.refund.refundLineItems.edges)
correctResp.push(...LineItemsHasNextFalse.data.refund.refundLineItems.edges)
stub.getRefundItems.onCall(0).returns(LineItemsHasNextTrue)
stub.getRefundItems.onCall(1).returns(LineItemsHasNextFalse)
let resp = await refunds.getRefundItems({"gid":"shjdjs"})
console.log(resp)
expect(resp).to.deep.equal(correctResp)
}))
})
})
async getRefundItems(msgObj)
{
let hasNext = false
let items = []
let count = 0
do
{
let response = await stub.getRefundItems(msgObj)
if(response.data.refund != null)
{
items.push(...response.data.refund.refundLineItems.edges)
if(response.data.refund.refundLineItems.pageInfo.hasNextPage)
{
let cursor = response.data.refund.refundLineItems.edges[0].cursor
msgObj.after = cursor
hasNext = true
}
else
{
hasNext=false
}
}
}
while(hasNext)
return items
}
It tells me "promise is not a function". My problem is that with isomorphic fetch you have to put twice then to get your parsed result. What should I do to manage that properly with redux-saga generators ?
import { put, call, takeEvery, takeLatest } from 'redux-saga/effects'
import fetch from 'isomorphic-fetch'
import errorMessages from './../conf/errorMessages'
function *fetchBalances(address) {
try {
var request = fetch('/api/getBalances/rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr').then(function(response) {
return response.json();
}). then(function(result) {
// finally my parsed result !
return result;
});
const balances = yield call(request)
yield put({ type: 'GET_BALANCES_SUCCEED', balances: balances})
}
catch(error) {
yield put({ type: 'GET_BALANCES_ERROR', error: error })
}
}
export function* watchGetBalances() {
yield takeEvery('GET_BALANCES', fetchBalances);
}
I could put that in a closure but is that the best idea ? =/
var request = function() {
return fetch('/api/getBalances/rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr').then(function(response) {
return response.json();
}). then(function(result) {
return result;
});
}
The confusion here comes from the fact that the request variable that you're passing to the call effect holds a Promise. What you want, is the call effect to execute that Promise for you.
In other words, you have already manually called the fetch function, instead of passing call a function that will call and return it.
So, yes, wrapping your fetch calls in a, eg. callRequest function could be useful but if you do so, you must be careful to write const response = yield call(callRequest), and not const response = yield call(callRequest()), which would be equivalent to what you wrote.
A few precisions, in case it helps. From the docs, call receives as its first argument:
A Generator function, or normal function which either returns a Promise as result, or any other value.
Let's see how that works.
First a generator function.
const response = yield call(myFunction);
function* myFunction() {
yield delay(1000);
return true;
}
// response === true
Then a function returning some value (not the most useful, but…)
const response = yield call(myFunction);
function myFunction() {
return true;
}
// response === true;
Finally a function returning a Promise
const response = yield call(callRequest);
function callRequest() {
return fetch(…).then( r => r.json() )
}
// response holds your data
// if the Promise were to be rejected, you could catch
// the error in a try-catch block
I need to prevent my meteor helper from returning right away until either the timeout or data returns from my Meteor.http.get(url, ...) request. For example,
Meteor.templateName.helpers ({
testHelper: function()
{
var ss = "doesnt wait";
Meteor.http.get("http://api.somesite.com",
function (error, result) {
if(!error){
if(result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson);
ss = "should have this value";
}
}
});
return ss;
}
})
Is Meteor.http not a blocking call, how can i make the helper method stop until the get request returns data. Do i need to move the get request to a Meteor.Method ?
On the client, you don't have the fiber module, as a result is not possible to make a synchronous call to a function.
One solution might be to use a Session because of it's reactivity. You just set a default value, and use it in your helper function
Session.setDefault('testHelper', {msg: 'wait'})
Meteor.templateName.helpers ({
testHelper: function() {
return Session.get('testHelper');
}
});
Then update this session every time you want:
Template.templateName.rendered = function () {
Meteor.http.get("http://api.somesite.com",
function (error, result) {
if(!error && result.statusCode === 200){
var respJson = JSON.parse(result.content);
Session.set('testHelper', respJson)
}
}
);
}
If you don't want to use a Session, you can implement your own reactivity mecanism using the Deps module. For instance in the Meteor.http.get callback you can set a Template.templateName attribute and invalidate a context object in order to rerun the helper function. But a Session is definitly more easy ;-)
I'm trying to use $http, but why it return null result?
angular.module('myApp')
.factory('sender', function($http) {
var newData = null;
$http.get('test.html')
.success(function(data) {
newData = data;
console.log(newData)
})
.error(function() {
newData = 'error';
});
console.log(newData)
return newData
})
Console say: http://screencast.com/t/vBGkl2sThBd4. Why my newData first is null and then is defined? How to do it correctly?
As YardenST said, $http is asynchronous so you need to make sure that all functions or display logic that are dependent on the data that is returned by your $http.get(), gets handle accordingly. One way to accomplish this is to make use of the "promise" that $http returns:
Plunkr Demo
var myApp = angular.module('myApp', []);
myApp.factory('AvengersService', function ($http) {
var AvengersService = {
getCast: function () {
// $http returns a 'promise'
return $http.get("avengers.json").then(function (response) {
return response.data;
});
}
};
return AvengersService;
});
myApp.controller('AvengersCtrl', function($scope, $http, $log, AvengersService) {
// Assign service to scope if you'd like to be able call it from your view also
$scope.avengers = AvengersService;
// Call the async method and then do stuff with what is returned inside the function
AvengersService.getCast().then(function (asyncCastData) {
$scope.avengers.cast = asyncCastData;
});
// We can also use $watch to keep an eye out for when $scope.avengers.cast gets populated
$scope.$watch('avengers.cast', function (cast) {
// When $scope.avengers.cast has data, then run these functions
if (angular.isDefined(cast)) {
$log.info("$scope.avengers.cast has data");
}
});
});
This JavaScript code is asynchronous.
console.log(newData)
return newData
Is executed before what inside success
newData = data;
console.log(newData)
So at first time, the newData is null (you set it to be null)
And when the http response is returned (inside the success), the newData gets its new value.
This is very common in Javascript, you should do all your work inside the success.