Forums

Posting in these forums is disabled. These forums will be available for archive purposes. Please join the new forums at the links below:

  • yui-support - replaces the `YUI 3.x` and `YUI 3 Gallery` forums.
    We have created the following discussion categories within this group to aid discoverability for these most-used topics:
    • Charts for YUI Charts support.
    • DataTable for YUI DataTable support.
    • Gallery for YUI Gallery support, including support for published Gallery components as well as the Gallery process in general.
    • Tools for support of YUI’s suite of developer tools such as selleck, shifter, grover, yogi, etc.
    • Everything Else for questions that don’t fit one of the categories above, we’ve got you covered here.
  • yui-deprecated - replaces the `YUI 2.x` forum and the forums of other deprecated products (`YUI Doc`, `Builder`, `YUI PHP Loader`, etc.).
  [ 10 posts ]
New Topic | Post Reply | Print view
Previous topic | Next topic

Nate Cavanaugh

YUI Contributor

  • Offline
  • Profile
Tags:
  • github
  • loader
  • queue
  • utility

Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 6:28 am
+0-
Hi guys,
I've recently created a module that I would normally submit to the gallery, but it's handling a use case that wouldn't fit too well into the gallery.

The github repo is here: http://github.com/natecavanaugh/yui-provide

Basically, I'm looking for feedback and thoughts, and if anyone else has run into a similar problem and has thought of any other ways to solve the issue (especially with elegant ways of returning data).

And I'll paste from readme here incase you don't want to go over there:

A utility to allow you to create globally accessible functions that have YUI dependencies that will be loaded asynchronously.

For example, if you create

Code:
function addXY(x, y) {
   YUI().Module1(x) + YUI().Module2(y);
}


This function may need to use multiple modules and load them in asynchronously. However, the function may need to be called globally at any time or it may need to be called multiple times.

There are two issues here:
If you do
Code:
YUI().use('module1', 'module2', function(Y){
   function addXY(x, y) {
      Y.Module1(x) + Y.Module2(y);
   }
});


And a user tries to call addXY() before module1 and module2 have been loaded, then an error will be thrown.

However, if you do
Code:
function addXY(x, y) {
   YUI().use('module1', 'module2', function(Y){
      Y.Module1(x) + Y.Module2(y);
   });
}


Then every time the user calls addXY, it will have to go through the loader, and this can get slow pretty quickly (roughly 40-50ms for every call).
It's a small performance hit for 1 or two calls, but if it's a function that could be multiple times, 50ms * 5 becomes quite noticeable.

What this does
=========================================

You define a function like this:

Code:
YUI.provide(window, 'addXY', function(x, y){
   Y.Module1(x) + Y.Module2(y);
}, ['module1', 'module2']);


and you can call it anywhere exactly like you would like any normal function:

Code:
addXY(1, 2);

You could also run it multiple times, and the loader will only be called once for that function.

Internally, it will queue up the function calls until the loader has finished and then fire them all through the queue.

After the loader loads it's module list, the function will never go through the loader again.


Downsides to this method
=========================================

There is one downside to this method, which is that functions cannot return data. This is because of the async nature of the functions.
The work around I've come up with is to define your function with a callback like so:

Code:
YUI.provide(window, 'addXY', function(x, y, callback){
   if (callback) {
      callback(Y.Module1(x) + Y.Module2(y));
   }
}, ['module1', 'module2']);

The other "downside", but could be changed, is that this function currently requires the oop module to be preloaded onto the page.
Nate Cavanaugh

Matt Parker

YUI Contributor

  • Username: mattatlamplight
  • Joined: Mon Apr 20, 2009 12:03 pm
  • Posts: 473
  • Location: London UK
  • GitHub: mattparker
  • Gists: mattparker
  • Offline
  • Profile
Tags:
  • examples

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 7:12 am
+0-
Nate,

Do you want return values out of addXY? Your first two code examples don't have them. So there's another issue with the second, I think: you can't have

Code:
function addXY(x, y) {
   YUI().use('module1', 'module2', function(Y){
      return Y.Module1(x) + Y.Module2(y);
   });
}


because the YUI().use() doesn't return the sum, so even if the loader penalty was acceptable I don't think it'd work.

But I'm trying something else... will post again if it comes to anything!

Matt

Matt Parker

YUI Contributor

  • Username: mattatlamplight
  • Joined: Mon Apr 20, 2009 12:03 pm
  • Posts: 473
  • Location: London UK
  • GitHub: mattparker
  • Gists: mattparker
  • Offline
  • Profile

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 8:17 am
+0-
OK, I was trying this
Code:
var addXY = (function() {
   
    var f;

    return function (x,y) {
      if (f === undefined) {
        YUI().use('module1', 'module2', function(Y){
             f = function (x,y) {
                return Y.Module1(x) + Y.Module2(y);
             }
        });

      } else {
        return f(x,y);
      }
    };
   
  }());


But it doesn't work until the loader's done, so gives errors (although it'll only load once).

I suppose the callback is inevitable.

Matt

Matt Parker

YUI Contributor

  • Username: mattatlamplight
  • Joined: Mon Apr 20, 2009 12:03 pm
  • Posts: 473
  • Location: London UK
  • GitHub: mattparker
  • Gists: mattparker
  • Offline
  • Profile

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 8:23 am
+0-
So actually is this any good?

Code:
var addXY = (function() {
   
    var f;

    return function (x,y,callback) {
      if (f === undefined) {
        YUI().use('module1', 'module2', function(Y){
             f = function (x,y) {
                return Y.Module1(x) + Y.Module2(y);
             }
          if (callback) {
             callback( f(x,y) );
           }
        });

      } else {
         if (callback) {
           callback( f(x,y) );
         }
        return f(x,y);
      }
    };
   
  }());


Here callback is passed the result of the calculation.

Matt

Caridy Patino

YUI Contributor

  • Username: caridy
  • Joined: Mon Dec 08, 2008 5:40 pm
  • Posts: 494
  • Location: Miami, FL
  • Twitter: caridy
  • GitHub: caridy
  • Gists: caridy
  • IRC: caridy
  • YUI Developer
  • Offline
  • Profile
Tags:
  • yui
  • build
  • gallery
  • config
  • global

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 8:29 am
+0-
Hey Nate,

Let's try to separate this into two questions:

1. How the gallery can play nicely with a component that should not be wrapped within YUI.add() call during the build process.

2. How to implement gallery-provide to fulfill your use-case.

For me, #1 is a valid question, and I have a similar issue with gallery-event-binder. I talked to Adam and Dav about it, and hopefully we will have a solution for that use-case some time soon. In my case I want to define a gallery module to augment the default YUI_config object, and even declaring or calling some global methods before YUI seed become available in the page.

Now, about #2, I didn't understand the whole use-case, can you give us a more realistic example, and more details about the end goal?

Best Regards,
Caridy

Nate Cavanaugh

YUI Contributor

  • Offline
  • Profile

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 8:56 am
+0-
Hi Matt,
I would love to be able to at least have the option to return data from the function.

I had talked it over with the YUI team briefly before, and Luke mentioned using promises which might be the only other way to handle it, which isn't quite the same.

So far the downside with the callback mechanism is when you have not only nested callbacks, but conditional callbacks.

If you have addXY and addAB and both are async functions that accept a callback, but you need to only run them conditionally, it becomes a nightmare to start to run them together.

For example:

addXY(x,y, function(value1){
input1.value = value1;
addAB(a, b, function(value2){
input2.value = value2;
submitForm();
});
});

This works okay (besides being wordy). But if you need to conditionally handle this, it gets hairy:

if(input1){
var finished = false;
addXY(x, y, function(value1) {
input1.value = value1;
finished = true;
});
}

addAB(a, b, function(value2) {
input2.value = value2;
if((input1 && finished) || !input1){
submitForm();
}
});


Which is a bit of a nightmare, especially if there are more conditions.

This would be the perfect case for an AsyncQueue. The previous would be done as:

var aq = new Y.AsyncQueue();

if(input1){
aq.add(function(){
aq.pause();
addXY(x, y, function(value1) {
input1.value = value1;
aq.run();
});
});
}

aq.add(function(){
aq.pause();
addAB(a, b, function(value2) {
input2.value = value2;
aq.run();
});
});

aq.add(submitForm);

aq.run();

The downside to this is the amount of code, but at least it's not an inflated amount of logic.

I'm still looking at trying out an elegant promises API.

Let me know if you have any ideas, I'd love to hear them :)

Matt Parker

YUI Contributor

  • Username: mattatlamplight
  • Joined: Mon Apr 20, 2009 12:03 pm
  • Posts: 473
  • Location: London UK
  • GitHub: mattparker
  • Gists: mattparker
  • Offline
  • Profile
Tags:
  • asyncqueue

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 11:04 am
+0-
Still not elegant, but perhaps easier to handle multiple conditions:

Code:
var toDo = 0;

if(input1){
  toDo ++;
  addXY(x, y, function(value1) {
    input1.value = value1;
    toDo --;
  });
}

addAB(a, b, function(value2) {
  input2.value = value2;
  if (toDo === 0) {
    submitForm();
  }
});


But it does feel like there's a nicer way.

But if you've got an AsyncQueue, aren't you in a YUI().use that can also include the modules you need up front, so you know addXY is ready and will return a value directly? Or is that the point - you're not in one so you can't use AsyncQueue?

Matt

Nate Cavanaugh

YUI Contributor

  • Offline
  • Profile
Tags:
  • general
  • utility

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 12:14 pm
+0-
@Caridy:

I agree, separating it will help make it clearer:

1. I totally agree. I don't know how this would be done, but yeah, it would be very nice :)
It was tangential to the request for feedback though. I'm looking more for feedback on the code architecture.

2. This one has morphed during this thread, and there are two topics being discussed here:
2a. Creating statically called global functions which depend on YUI modules in a performant manner, in a patternized way.
2b. Returning data from those kind of functions.

2a. The module I am showing off as my solution is the YUI.provide.
Basically, you may want to create a global function where you don't know which YUI modules have been loaded, or want to trim down to a small seed file. These might be general utility functions in a webapp that do some sort of work.

2b. Sometimes these utility functions may return data, and I'm just trying to think of an elegant way to handle that use case (and callbacks and the occasional async-queue may be it).

Does that make sense?

@Matt:
While we may be able to know that async-queue is loaded, addXY is defined using the YUI.provide which has it's own set of dependencies which we don't know at call time.
So we won't know which ones to load when it's called.

Caridy Patino

YUI Contributor

  • Username: caridy
  • Joined: Mon Dec 08, 2008 5:40 pm
  • Posts: 494
  • Location: Miami, FL
  • Twitter: caridy
  • GitHub: caridy
  • Gists: caridy
  • IRC: caridy
  • YUI Developer
  • Offline
  • Profile

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 12:37 pm
+0-
Hey Nate,

About #2, I think you can follow the pattern described in this article by Dustin:
http://www.dustindiaz.com/async-method-queues/
within the boundaries of YUI.

Still I don't understand why are you mixing GLOBALS with this use case. For me, this just add complexity to the use-case. Can you describe this within the boundaries of YUI?

Best Regards,
Caridy

Nate Cavanaugh

YUI Contributor

  • Offline
  • Profile
Tags:
  • collection
  • general
  • loader
  • node
  • substitute
  • utility

Re: Request for feedback: static fns with async dependencies

Post Posted: Wed Jun 09, 2010 8:09 pm
+0-
Sorry, I was using global misleadingly. I've read Dustin's article, but it's not quite what I'm thinking of. Or maybe it is, and what I've done is just written a specific way to create these queues for functions which depend on YUI modules.

Let's say I want a utility function that is always statically loaded on the YUI object like: YUI.namespace('myApp').addXY(x,y);
that requires the "collection" package (for whatever reason).

As I mentioned in my first example, there is really no way to use this method without either requiring the loader to be checked against every time it's called, or for it to run into errors.

You might be thinking:
If you want a these utility functions, why not just create a utility module that requires collection?

Here's a better use case:

Let's say I have a disparate set of functions that require many different modules.

For instance, let's say I have:

YUI.myApp.registerNode();//requires node

YUI.myApp.doWork();//requires array-list

YUI.myApp.formatStr();//requires substitute

etc.

For whatever reason, I may need to include these on the page to always be available (and added to that, requiring a .use() call for every small function invocation can be quite a burden to require developers to write when they just want to write a function call).

This is where the YUI.provide method would come in:

It helps you define a function where you specify the modules it will need to load, and it will only load them on the first call, and swap out the function as soon as the modules have been loaded.

Here's how I would compare the the typical way against the way I'm proposing with YUI.provide:
Let's say I need to create a function to be used in YUI.

Code:
//--------Define function
YUI.add('myapp-do-work', function(Y){
YUI.myApp.doWork = function(){
//Do work right here
}
}, '', {requires: ['array-list']});

//--------Use function
YUI().use('myapp-do-work', function(Y){
YUI.myApp.doWork();
});


vs.

Code:
//--------Define function
YUI.provide(YUI.myApp, 'doWork', function(){
// Do work
},
['array-list']);

//--------Use function
YUI.myApp.doWork();


I would say that in the YUI.provide portion, the code pieces are much smaller on both ends, and a bit easier to read.
I'm not recommending this pattern over module creation in general, but more or less to offer this as one way to possibly create functions that can be called without having to write the wrapper code for either the add or the invocation.

Does all of that make sense?

And do you see perhaps a better pattern for handling this?

Our use case is that we have quite a lot of small utility functions (checking all checkboxes based on the checked state of one checked box, sorting select boxes, , etc) that not only don't fit into any one module, but are called in such a way that requiring our developers to write the .use('module') just to be able to call the function would be a bit of a burden.

Does that use case make sense? Do you know of better ways to handle this?
Nate Cavanaugh
  [ 10 posts ]
New Topic | Post Reply | Print view
Previous topic | Next topic
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum