Guzzle Pool in Symfony 2 - Dealing with future - symfony

I am trying to use Guzzle pool in Symfony 2 application. I am thinking to use it because its capability of sending concurrent request at once.
However, since its async in nature I am not sure how can I use it as service in Symfony 2. Since return are not always immediate.
For example lets say I have service called Foo in Symfony which have method like this some what.
function test()
{
$request = $client->createRequest('GET', 'http://lt/?n=0', ['future' => true]);
$client->send($request)->then(function ($response) {
return "\n".$response->getBody();
});
}
Now I invoke this service like this.
$service = $this->get('foo');
$result = $service->test();
echo $result;// does not work :( echoes out null
Is there any way to get around this problem. I really want to use Future since I need async feature.

You have to deal with promises (futures) in your application or wait for results.
About your example: firstly, you don't return anything from test() ;) Because of that you don't get any $result. As I said before, you have to choose between two different ways here: 1) wait for HTTP call inside test() method and return the result itself, or 2) return Promise immediately and deal with it in your app (through then(), otherwise() and wait() in the end).
If you choose async way for the whole app, the code might look like this:
function test()
{
return $client->getAsync('http://lt/?n=0')
->then(function ($response) {
return "\n".$response->getBody();
});
}
And later:
$service = $this->get('foo');
$promise = $service->test()->then(function ($responseString) {
echo $responseString;
});
$promise->wait(); // Here you get the output.
I slightly changed you code for Guzzle 6 (current version of Guzzle that should use in new projects).
BTW, Guzzle uses its own implementation of promises, that isn't connected with React.

Related

Asynchronous execution of a function App Script

I've been digging around, and I'm not able to find references or documentation on how I can use Asynchronous Functions in Google App Script, I found that people mention It's possible, but not mention how...
Could someone point me in the right direction or provide me with an example?
Promises, Callbacks, or something, that can help me with this.
I have this function lets call it foo that takes a while to execute (long enough that It could time out an HTTP call).
What I'm trying to do Is to refactor it, in a way that it works like this:
function doPost(e) {
// parsing and getting values from e
var returnable = foo(par1, par2, par3);
return ContentService
.createTextOutput(JSON.stringify(returnable))
.setMimeType(ContentService.MimeType.JSON);
}
function foo(par1, par2, par3) {
var returnable = something(par1, par2, par3); // get the value I need to return;
// continue in an Async way, or schedule execution for something else
// and allow the function to continue its flow
/* async bar(); */
return returnable;
}
Now I want to realize that bit in foo because It takes to long and I don't want to risk for a time out, also the logic that occurs there it's totally client Independent, so It doesn't matter, I just need the return value, that I'll be getting before.
Also, I think It's worth mentioning that this is deployed in Google Drive as a web app.
It's been long since this, but adding some context, at that moment I wanted to scheduled several things to happen on Google Drive, and It was timing out the execution, so I was looking for a way to safely schedule a job.
You want to execute functions by the asynchronous processing using Google Apps Script.
You want to run the functions with the asynchronous processing using time trigger.
If my understanding is correct, unfortunately, there are no methods and the official document for directly achieving it. But as a workaround, that can be achieved by using both Google Apps Script API and the fetchAll method which can work by asynchronous processing.
The flow of this workaround is as follows.
Deploy API executable, enable Google Apps Script API.
Using fetchAll, request the endpoint of Google Apps Script API for running function.
When several functions are requested once, those work with the asynchronous processing by fetchAll.
Note:
I think that Web Apps can be also used instead of Google Apps Script API.
In order to simply use this workaround, I have created a GAS library. I think that you can also use it.
In this workaround, you can also run the functions with the asynchronous processing using time trigger.
References:
fetchAll
Deploy the script as an API executable
scripts.run of Google Apps Script API
Benchmark: fetchAll method in UrlFetch service for Google Apps Script
GAS library for running the asynchronous processing
If I misunderstand your question, I'm sorry.
There is another way to accomplish this.
You can use time-based one-off triggers to run functions asynchronously, they take a bit of time to queue up (30-60 seconds) but it is ideal for slow-running tasks that you want to remove from the main execution of your script.
// Creates a trigger that will run a second later
ScriptApp.newTrigger("myFunction")
.timeBased()
.after(1)
.create();
There is handy script that I put together called Async.gs that will help remove the boilerplate out of this technique. You can even use it to pass arguments via the CacheService.
Here is the link:
https://gist.github.com/sdesalas/2972f8647897d5481fd8e01f03122805
// Define async function
function runSlowTask(user_id, is_active) {
console.log('runSlowTask()', { user_id: user_id, is_active: is_active });
Utilities.sleep(5000);
console.log('runSlowTask() - FINISHED!')
}
// Run function asynchronously
Async.call('runSlowTask');
// Run function asynchronously with one argument
Async.call('runSlowTask', 51291);
// Run function asynchronously with multiple argument
Async.call('runSlowTask', 51291, true);
// Run function asynchronously with an array of arguments
Async.apply('runSlowTask', [51291, true]);
// Run function in library asynchronously with one argument
Async.call('MyLibrary.runSlowTask', 51291);
// Run function in library asynchronously with an array of arguments
Async.apply('MyLibrary.runSlowTask', [51291, true]);
With the new V8 runtime, it is now possible to write async functions and use promises in your app script.
Even triggers can be declared async! For example (typescript):
async function onOpen(e: GoogleAppsScript.Events.SheetsOnOpen) {
console.log("I am inside a promise");
// do your await stuff here or make more async calls
}
To start using the new runtime, just follow this guide. In short, it all boils down to adding the following line to your appsscript.json file:
{
...
"runtimeVersion": "V8"
}
Based on Tanaike's answer, I created another version of it. My goals were:
Easy to maintain
Easy to call (simple call convention)
tasks.gs
class TasksNamespace {
constructor() {
this.webAppDevUrl = 'https://script.google.com/macros/s/<your web app's dev id>/dev';
this.accessToken = ScriptApp.getOAuthToken();
}
// send all requests
all(requests) {
return requests
.map(r => ({
muteHttpExceptions: true,
url: this.webAppDevUrl,
method: 'POST',
contentType: 'application/json',
payload: {
functionName: r.first(),
arguments: r.removeFirst()
}.toJson(),
headers: {
Authorization: 'Bearer ' + this.accessToken
}
}), this)
.fetchAll()
.map(r => r.getContentText().toObject())
}
// send all responses
process(request) {
return ContentService
.createTextOutput(
request
.postData
.contents
.toObject()
.using(This => ({
...This,
result: (() => {
try {
return eval(This.functionName).apply(eval(This.functionName.splitOffLast()), This.arguments) // this could cause an error
}
catch(error) {
return error;
}
})()
}))
.toJson()
)
.setMimeType(ContentService.MimeType.JSON)
}
}
helpers.gs
// array prototype
Array.prototype.fetchAll = function() {
return UrlFetchApp.fetchAll(this);
}
Array.prototype.first = function() {
return this[0];
}
Array.prototype.removeFirst = function() {
this.shift();
return this;
}
Array.prototype.removeLast = function() {
this.pop();
return this;
}
// string prototype
String.prototype.blankToUndefined = function(search) {
return this.isBlank() ? undefined : this;
};
String.prototype.isBlank = function() {
return this.trim().length == 0;
}
String.prototype.splitOffLast = function(delimiter = '.') {
return this.split(delimiter).removeLast().join(delimiter).blankToUndefined();
}
// To Object - if string is Json
String.prototype.toObject = function() {
if(this.isBlank())
return {};
return JSON.parse(this, App.Strings.parseDate);
}
// object prototype
Object.prototype.toJson = function() {
return JSON.stringify(this);
}
Object.prototype.using = function(func) {
return func.call(this, this);
}
http.handler.gs
function doPost(request) {
return new TasksNamespace.process(request);
}
calling convention
Just make arrays with the full function name and the rest are the function's arguments. It will return when everything is done, so it's like Promise.all()
var a = new TasksNamespace.all([
["App.Data.Firebase.Properties.getById",'T006DB4'],
["App.Data.External.CISC.Properties.getById",'T00A21F', true, 12],
["App.Maps.geoCode",'T022D62', false]
])
return preview
[ { functionName: 'App.Data.Firebase.Properties.getById',
arguments: [ 'T006DB4' ],
result:
{ Id: '',
Listings: [Object],
Pages: [Object],
TempId: 'T006DB4',
Workflow: [Object] } },
...
]
Notes
it can handle any static method, any method off a root object's tree, or any root (global) function.
it can handle 0 or more (any number) of arguments of any kind
it handles errors by returning the error from any post
// First create a trigger which will run after some time
ScriptApp.newTrigger("createAsyncJob").timeBased().after(6000).create();
/* The trigger will execute and first delete trigger itself using deleteTrigger method and trigger unique id. (Reason: There are limits on trigger which you can create therefore it safe bet to delete it.)
Then it will call the function which you want to execute.
*/
function createAsyncJob(e) {
deleteTrigger(e.triggerUid);
createJobsTrigger();
}
/* This function will get all trigger from project and search the specific trigger UID and delete it.
*/
function deleteTrigger(triggerUid) {
let triggers = ScriptApp.getProjectTriggers();
triggers.forEach(trigger => {
if (trigger.getUniqueId() == triggerUid) {
ScriptApp.deleteTrigger(trigger);
}
});
}
While this isn't quite an answer to your question, this could lead to an answer if implemented.
I have submitted a feature request to Google to modify the implementation of doGet() and doPost() to instead accept a completion block in the functions' parameters that we would call with our response object, allowing additional slow-running logic to be executed after the response has been "returned".
If you'd like this functionality, please star the issue here: https://issuetracker.google.com/issues/231411987?pli=1

Converting a collection of Awaitables to a time-ordered AsyncGenerator in Hack

I am trying to implement Rx stream/observable merging with Hack async, and a core step is described by the title. A code version of this step would look something like this:
<?hh // strict
async function foo(Awaitable<Iterable<T>> $collection): Awaitable<void> {
$ordered_generator = async_collection_to_gen($collection) // (**)
foreach($ordered_generator await as $v) {
// do something with each awaited value in the time-order they are resolved
}
}
However, after mulling it over, I don't think I can write the starred (**) function. I've found that at some point or another, the implementations I've tried require functionality akin to JS's Promise.race, which resolves when the first of a collection Promises resolves/rejects. However, all of Hack's Awaitable collection helpers create an Awaitable of a fully resolved collection. Furthermore, Hack doesn't permit that we don't await async calls from async functions, which I've also found to be necessary.
Is it possible to anyone's knowledge?
This is possible actually! I dug around and stumbled upon a fork of asio-utilities by #jano implementing an AsyncPoll class. See PR for usage. It does exactly as I hoped.
So it turns out, there is an Awaitable called ConditionWaitHandle with succeed and fail methods* that can be invoked by any context (so long as the underlying WaitHandle hasn't expired yet), forcing the ConditionWaitHandle to resolve with the passed values.
I gave the code a hard look, and underneath it all, it works by successive Awaitable races, which ConditionWaitHandle permits. More specifically, the collection of Awaitables is compressed via AwaitAllWaitHandles (aka \HH\Asio\v) which resolves as slowly as the slowest Awaitable, then nested within a ConditionWaitHandle. Each Awaitable is awaited in an async function that triggers the common ConditionWaitHandle, concluding the race. This is repeated until the Awaitables have all resolved.
Here's a more compact implementation of a race using the same philosophy:
<?hh
function wait(int $i): Awaitable<void> {
return Race::wrap(async { await HH\Asio\usleep($i); return $i; });
}
// $wait_handle = null;
class Race {
public static ?ConditionWaitHandle $handle = null;
public static async function wrap<T>(Awaitable<T> $v): Awaitable<void> {
$ret = await $v;
$handle = self::$handle;
invariant(!is_null($handle), '');
$handle->succeed($ret);
}
}
Race::$handle = ConditionWaitHandle::create(
\HH\Asio\v(
Vector{
(wait(1))->getWaitHandle(),
(wait(1000000))->getWaitHandle()
}
)
);
printf("%d microsecond `wait` wins!", \HH\Asio\join(Race::$handle));
Very elegant solution, thanks #jano!
*(the semblance to promises/deferred intensifies)
I am curious how premature completion via ConditionWaitHandle meshes with the philosophy that all Awaitables should run to completion.

Test an async PipeTransform

Context
I have a basic PipeTransform, expect the fact that it is async. Why? because I have my own i18n service (because of parsing, pluralization and other constraints, I did my own) and it returns a Promise<string>:
#Pipe({
name: "i18n",
pure: false
})
export class I18nPipe implements PipeTransform {
private done = false;
constructor(private i18n:I18n) {
}
value:string;
transform(value:string, args:I18nPipeArgs):string {
if(this.done){
return this.value;
}
if (args.plural) {
this.i18n.getPlural(args.key, args.plural, value, args.variables, args.domain).then((res) => {
this.value = res;
this.done = true;
});
}
this.i18n.get(args.key, value, args.variables, args.domain).then((res) => {
this.done = true;
this.value = res;
});
return this.value;
}
}
This pipe works well, because the only delayed call is the very first one (the I18nService uses lazy loading, it loads JSON data only if the key is not found, so basically, the first call will be delayed, the other ones are instant but still async).
Problem
I can't figure out how to test this pipe using Jasmine, since it is working inside a component I know it works, but the goal here is to get this fully tested using jasmine, this way I can add it to a CI routine.
The above test:
describe("Pipe test", () => {
it("can call I18n.get.", async(inject([I18n], (i18n:I18n) => {
let pipe = new I18nPipe(i18n);
expect(pipe.transform("nope", {key: 'test', domain: 'test domain'})).toBe("test value");
})));
});
Fails because since the result given by the I18nService is async, the returned value is undefined in a sync logic.
I18n Pipe test can call I18n.get. FAILED
Expected undefined to be 'test value'.
EDIT: One way to do it would be to use setTimeout but I want a prettier solution, to avoid adding setTimeout(myAssertion, 100) everywhere.
Use fakeAsync from #angular/core/testing. It allows you to call tick(), which will wait for all currently queued asynchronous tasks to complete before continuing. This gives the illusion of the actions being synchronous. Right after the call to tick() we can write our expectations.
import { fakeAsync, tick } from '#angular/core/testing';
it("can call I18n.get.", fakeAsync(inject([I18n], (i18n:I18n) => {
let pipe = new I18nPipe(i18n);
let result = pipe.transform("nope", {key: 'test', domain: 'test domain'});
tick();
expect(result).toBe("test value");
})));
So when should we use fakeAsync and when should we use async? This is the rule of thumb that I go by (most of the time). When we are making asynchronous calls inside the test, this is when we should use async. async allows to test to continue until all asynchronous calls are complete. For example
it('..', async(() => {
let service = new Servce();
service.doSomething().then(result => {
expect(result).toBe('hello');
});
});
In a non async test, the expectation would never occur, as the test would complete before the asynchronous resolution of the promise. With the call to async, the test gets wrapped in a zone, which keeps track of all asynchronous tasks, and waits for them to complete.
Use fakeAsync when the asynchronous behavior is outside the control of the test (like in your case is going on in the pipe). Here we can force/wait for it to complete with the call to tick(). tick can also be passed a millisecond delay to allow more time to pass if needed.
Another option is to mock the service and make it synchronous, as mentioned in this post. When unit testing, if your components in test are dependent on heavy logic in the service, then the component in test is at the mercy of that service working correctly, which kinda defeats the purpose of a "unit" test. Mocking makes sense in a lot of cases.

How to use wrapAsync in Meteor

I am having trouble figuring out how to use wrapAsync properly using Meteor. I am currently working with node-apac and the Amazon Product Advertising API.
If am trying to run the following code, how do I run it asynchronously:
opHelper.execute('ItemSearch', {
'SearchIndex': 'Books',
'Keywords': 'harry potter',
'ResponseGroup': 'ItemAttributes, Offers'
}, function(err, results) {
console.log(results);
});
I have tried to watch several videos, but am having trouble
Meteor.wrapAsync takes an asynchronous method like opHelper.execute and makes it synchronous. It can do this so long as the last param taken by the method returns a callback where the first param is the error and second the result. This is just like your method!
You make a new method that is synchronous:
var opExecuteSynchronous = Meteor.wrapAsync(opHelper.Execute, opHelper);
The first param opHelper.Execute is the method you want to make asynchronous, and the second param is the context of the method (opHelper).
You can use this synchronously now:
var results = opExecuteSynchronous('ItemSearch', {
'SearchIndex': 'Books',
'Keywords': 'harry potter',
'ResponseGroup': 'ItemAttributes, Offers'
})
This will throw an error if err is called instead of results in the callback.

How to GetLocalizedItems using Tridion Anguilla Framework?

I would like to use the GetLocalizedItems method in the Anguilla Framework.
I don't know how to create a new filter and set the conditions or what to use for success and failure.
In the GUI:
tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(id, filter.conditions,
filter.columns, success, failure);
Are the methods in this namespace intended to be used by our extensions?
Building a filter
Here is an example of how to build a filter
var filter = new Tridion.ContentManager.ListFilter();
filter.conditions.ItemTypes = 16 | 2; // folders and components
filter.conditions.Recursive = true;
filter.conditions.BasedOnSchema = "tcm:1-23-8,tcm:1-32-8".split(",");
filter.columns = Tridion.Constants.ColumnFilter.DEFAULT;
Or this extremely simple case from General.js:
var templateFilter = new Tridion.ContentManager.ListFilter({
conditions: { ItemTypes: [ itemType ] }
});
Calling a WCF method
The second part of your question was really already covered in https://stackoverflow.com/a/9385975/209103, although I'll make it a bit more concrete here.
WCF/AJAX calls such as this are executed asynchronously, since they may take some time to complete. While you would normally simple handle the result of the call on the line after the closing parenthesis, you can't do that in AJAX calls since that line will be execute before the function has completed. Instead you have to pass in one or more callback functions that get called once the function completes.
I typically just pass in two functions that break into the JavaScript debugger of my browser when I first start figuring out such a method:
Tridion.Web.UI.ContentManager.WhereUsed.GetListUsedItems(
"tcm:1-23",
filter.conditions,
filter.columns,
new function() { console.log(arguments); debugger; },
new function() { console.log(arguments); debugger; }
);
So the first (anonymous) function is called when the (asynchronous) HTTP call to the TCM server succeeded, while the second is called when the call failed. In the answer I linked above, we called them onSuccess and onFailure to make their nature more explicit.
Both functions in this case simply write the implicit arguments parameter that is always passed in JavaScript. They then break into the JavaScript debugger of your browser, so you can inspect the arguments further.

Resources