Git Product home page Git Product logo

d3-queue's Introduction

d3-queue

A queue evaluates zero or more deferred asynchronous tasks with configurable concurrency: you control how many tasks run at the same time. When all the tasks complete, or an error occurs, the queue passes the results to your await callback. This library is similar to Async.js’s parallel (when concurrency is infinite), series (when concurrency is 1), and queue, but features a much smaller footprint: as of release 2, d3-queue is about 700 bytes gzipped, compared to 4,300 for Async.

Each task is defined as a function that takes a callback as its last argument. For example, here’s a task that says hello after a short delay:

function delayedHello(callback) {
  setTimeout(function() {
    console.log("Hello!");
    callback(null);
  }, 250);
}

When a task completes, it must call the provided callback. The first argument to the callback should be null if the task is successful, or the error if the task failed. The optional second argument to the callback is the return value of the task. (To return multiple values from a single callback, wrap the results in an object or array.)

To run multiple tasks simultaneously, create a queue, defer your tasks, and then register an await callback to be called when all of the tasks complete (or an error occurs):

var q = d3.queue();
q.defer(delayedHello);
q.defer(delayedHello);
q.await(function(error) {
  if (error) throw error;
  console.log("Goodbye!");
});

Of course, you can also use a for loop to defer many tasks:

var q = d3.queue();

for (var i = 0; i < 1000; ++i) {
  q.defer(delayedHello);
}

q.awaitAll(function(error) {
  if (error) throw error;
  console.log("Goodbye!");
});

Tasks can take optional arguments. For example, here’s how to configure the delay before hello and provide a name:

function delayedHello(name, delay, callback) {
  setTimeout(function() {
    console.log("Hello, " + name + "!");
    callback(null);
  }, delay);
}

Any additional arguments provided to queue.defer are automatically passed along to the task function before the callback argument. You can also use method chaining for conciseness, avoiding the need for a local variable:

d3.queue()
    .defer(delayedHello, "Alice", 250)
    .defer(delayedHello, "Bob", 500)
    .defer(delayedHello, "Carol", 750)
    .await(function(error) {
      if (error) throw error;
      console.log("Goodbye!");
    });

The asynchronous callback pattern is very common in Node.js, so Queue works directly with many Node APIs. For example, to stat two files concurrently:

d3.queue()
    .defer(fs.stat, __dirname + "/../Makefile")
    .defer(fs.stat, __dirname + "/../package.json")
    .await(function(error, file1, file2) {
      if (error) throw error;
      console.log(file1, file2);
    });

You can also make abortable tasks: these tasks return an object with an abort method which terminates the task. So, if a task calls setTimeout on start, it can call clearTimeout on abort. For example:

function delayedHello(name, delay, callback) {
  var id = setTimeout(function() {
    console.log("Hello, " + name + "!");
    callback(null);
  }, delay);
  return {
    abort: function() {
      clearTimeout(id);
    }
  };
}

When you call queue.abort, any in-progress tasks will be immediately aborted; in addition, any pending (not-yet-started) tasks will not be started. Note that you can also use queue.abort without abortable tasks, in which case pending tasks will be cancelled, though active tasks will continue to run. Conveniently, the d3-request library implements abort atop XMLHttpRequest. For example:

var q = d3.queue()
    .defer(d3.request, "http://www.google.com:81")
    .defer(d3.request, "http://www.google.com:81")
    .defer(d3.request, "http://www.google.com:81")
    .awaitAll(function(error, results) {
      if (error) throw error;
      console.log(results);
    });

To abort these requests, call q.abort().

Installing

If you use NPM, npm install d3-queue. If you use Bower, bower install d3-queue. Otherwise, download the latest release. You can also load directly from d3js.org, either as a standalone library or as part of D3 4.0. AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3 global is exported:

<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<script>

var q = d3.queue();

</script>

Try d3-queue in your browser.

API Reference

# d3.queue([concurrency]) <>

Constructs a new queue with the specified concurrency. If concurrency is not specified, the queue has infinite concurrency. Otherwise, concurrency is a positive integer. For example, if concurrency is 1, then all tasks will be run in series. If concurrency is 3, then at most three tasks will be allowed to proceed concurrently; this is useful, for example, when loading resources in a web browser.

# queue.defer(task[, arguments…]) <>

Adds the specified asynchronous task callback to the queue, with any optional arguments. The task is a function that will be called when the task should start. It is passed the specified optional arguments and an additional callback as the last argument; the callback must be invoked by the task when it finishes. The task must invoke the callback with two arguments: the error, if any, and the result of the task. To return multiple results from a single callback, wrap the results in an object or array.

For example, here’s a task which computes the answer to the ultimate question of life, the universe, and everything after a short delay:

function simpleTask(callback) {
  setTimeout(function() {
    callback(null, {answer: 42});
  }, 250);
}

If the task calls back with an error, any tasks that were scheduled but not yet started will not run. For a serial queue (of concurrency 1), this means that a task will only run if all previous tasks succeed. For a queue with higher concurrency, only the first error that occurs is reported to the await callback, and tasks that were started before the error occurred will continue to run; note, however, that their results will not be reported to the await callback.

Tasks can only be deferred before queue.await or queue.awaitAll is called. If a task is deferred after then, an error is thrown. If the task is not a function, an error is thrown.

# queue.abort() <>

Aborts any active tasks, invoking each active task’s task.abort function, if any. Also prevents any new tasks from starting, and immediately invokes the queue.await or queue.awaitAll callback with an error indicating that the queue was aborted. See the introduction for an example implementation of an abortable task. Note that if your tasks are not abortable, any running tasks will continue to run, even after the await callback has been invoked with the abort error. The await callback is invoked exactly once on abort, and so is not called when any running tasks subsequently succeed or fail.

# queue.await(callback) <>

Sets the callback to be invoked when all deferred tasks have finished. The first argument to the callback is the first error that occurred, or null if no error occurred. If an error occurred, there are no additional arguments to the callback. Otherwise, the callback is passed each result as an additional argument. For example:

d3.queue()
    .defer(fs.stat, __dirname + "/../Makefile")
    .defer(fs.stat, __dirname + "/../package.json")
    .await(function(error, file1, file2) { console.log(file1, file2); });

If all deferred tasks have already completed, the callback will be invoked immediately. This method may only be called once, after any tasks have been deferred. If this method is called multiple times, or if it is called after queue.awaitAll, an error is thrown. If the callback is not a function, an error is thrown.

# queue.awaitAll(callback) <>

Sets the callback to be invoked when all deferred tasks have finished. The first argument to the callback is the first error that occurred, or null if no error occurred. If an error occurred, there are no additional arguments to the callback. Otherwise, the callback is also passed an array of results as the second argument. For example:

d3.queue()
    .defer(fs.stat, __dirname + "/../Makefile")
    .defer(fs.stat, __dirname + "/../package.json")
    .awaitAll(function(error, files) { console.log(files); });

If all deferred tasks have already completed, the callback will be invoked immediately. This method may only be called once, after any tasks have been deferred. If this method is called multiple times, or if it is called after queue.await, an error is thrown. If the callback is not a function, an error is thrown.

d3-queue's People

Contributors

chaficnajjar avatar jasondavies avatar mbostock avatar mking avatar thiagopintodev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

d3-queue's Issues

Reusing queues

Sorry I post this here, but I didn't find a google group for this kind of issue.

I posted a question on SO a couple of days ago but it hasn't received much attention. The main idea is that I want to reuse a queue, but I realized that after using the queue more than one time, the q.awaitAll function is receiving previous results. This is the code:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
    </head>
    <body>
        <button id="send">Send</button>
    </body>
    <script>
        var button = document.querySelector('#send');
        var q = queue(1);
        var i = 0;
        button.addEventListener('click', function() {
            var message = i++;
            q.defer(function(message, callback) {
                setTimeout(function() {
                    callback(null, message);
                }, 3000);
            }, message);
        });

        q.awaitAll(function(err, result) {
            console.log(result);
        });
    </script>
</html>

After clicking the button once, I receive [0], but clicking the button again doesn't remove previous results (instead, I get [0,1]) and the array grows after each click.

Is this the intended behavior? Is there some way to reuse the queue?

Thanks

Feature request: cancel

It would be great to be able to do:

var q = queue(1);
tasks.forEach(function(t) { q.defer(t); });
q.awaitAll(function(error, results) { console.log("all done!"); });

// At a later time, before "all done!" is printed
q.cancel();

And all the remaining tasks become no-ops.

The keyword 'await' is reserved

Hey,

this line uses the name "await" as function name, even though it's a reserved keyword.

As mentioned in this issue at webpack, this causes problems when trying to parse the resulting d3 lib with a newer parser.

There were a few issues there (#55 and #58) containing the same problem, but the problem itself is not solved (or reoccured).

.progress() or .tick() method?

I often end up including code to track a slow queue's progress with some sort of tick function in each task. Would a PR with this be useful or too bloaty?

I'd imagine something like a .progress(onComplete) method that takes an onComplete function that receives the number of tasks completed so far:

myQueue.progress(function(completed){  console.log(completed, "tasks completed"); });

Array of queues with one awaitAll

Hi,

I'm a novice on this, so forgive my ignorance in many areas. I'm more an end user than a developer.

I used d3-queue to handle the loading of data from many url's and waiting for all data being read, before continuing with the plot. The common use.

However, I encountered an additional complication, because my traces may be loaded from many sources. For instance, because there is old data stored in one place, while new data comes from another.

I'm loading groups of traces from many urls, or sources. Let's say there are i sources. The traces data read from each source is stored in a specific array (the tableArray i, with j[i] elements). Then, the data is transferred to a master tracesArray, which has, let's say, k traces. Some traces may be sourced from many urls. So I need to queue the loading of read traces into the tracesArray, so that there is concurrency 1 for each particular trace, otherwise, the logic of ordering the traces data doesn't work, it cannot be done asynchronously. However, different traces may and should be loaded asynchronously into the tracesArray.

I think I could implement this as if there was one queue for each k trace. When all the k queues are finished I could continue the plotting of traces.

So the Idea would be to have a queue function with an array construction. Like defer[k] and one wait.

Do you think it would be a proper way of handling the problem, or is there an already known method for accomplishing what I need?

Uncaught TypeError: d3.queue is not a function

I'm loading in d3 and d3-queue

<script type="text/javascript" src="bower_components/d3/d3.js"></script>
<script type="text/javascript" src="bower_components/d3-queue/d3-queue.js"></script>

And trying to call d3.queue();

var q = d3.queue();

And I get this error:

Uncaught TypeError: d3.queue is not a function

Any ideas?

queue async continue on error

hi,
first great job I love it.
would it be possible to get results even when one or several errors have occured during process. it would be great
thanks,
julien

var q = queue(1);

q.defer(fnAsync1, {});
q.defer(fnAsync2; {}); // throw error
q.defer(fnAsync3, {});
q.awaitAll(function(error, results) {     
   // results from 1 and 3
    res.json(results);
  });

the retry function?

I am wondering whether we can add the failed task as one property of error, so that user can figure out which failed, also can use these try again.

Plus,now we don't return any success result if one of tasks fail, should we add at least return the success part of response ?

I don't know the exact purpose of this library, if this is desire ,I can make one PR.
Thanks
@mbostock

Re-submit job to queue again

Dear All,

I have an object which has a refresh method to create queue object and use defer method to submit jobs. I also implement awaitAll to emit an event. Everything works fine in my app first run. But when I try to rerun my refresh method, I found that all jobs did submit through defer (proved by logging each callback). But I cannot see awaitAll being called. How can I reset the queue to make awaitAll works when I re-submit jobs? Thanks for your advice.

q.length(), q.running()

first of all, nice small lib :)

It would be nice to be able to do q.length() to see how many tasks are queued, and q.running() / q.active() to see how many tasks are currently running.

How to load a mongoDB Meteor.js collection

I've struggled without success in loading a Meteor.js mongoDB collection using "queue.defer". I've found only one example the loads a mongoDB collection as follows:

queue()
.defer(d3.json, "/donorschoose/projects")
.await(makeGraphs);

my .defer code was -- .defer(d3.json, "/meteor/test") where "meteor" is the name of the mongoDB database and "test" is the name of the collection. I use Robomongo to view the "meteor" mongoDB database and its collections that include "test".

Any direction is appreciated.

Thanks... Jim

Why 'active' is not decreased? How to force the callback?

I would like to use queue in order to execute a long list of asyncronous tasks. I tryed as in the documentation:

var q = queue(1);
tasks.forEach(function(t) { q.defer(t); });
q.awaitAll(function(error, results) { console.log("all done!"); });

but in my case this code executes only the first task and the awaitAll function never runs. If I remove the queue parameter leaving simply queue() all the tasks are executed as aspected but the awaitAll method does not run.
I did other tests using the following code:

var q = queue(2); // With or without the parallelism
for (var i = 0; i < 5; i++) q.defer(function (x) {$("#test" + x).text(x);}, i);
q.awaitAll(function (error, results) {alert("all done!");});

You can play with this code here: http://jsfiddle.net/6p9Vz/ (if you leave a number 'n' for parallelism in queue call, only the content of n div will change. The alert never appear).
I opened the queue source code and the problem seems to be connected to the callback(i) function which is not called and thus the active counter is never decreased. Maybe the problem is the arguments passed to defer()?

Thank you
Michele

Cannot catch 422 errors

I cannot catch any XMLHttpRequest error from the defer method.

var q = d3.queue();
q.defer(d3.json, '/data/subpops.json'); // local json file
q.defer(d3.json, sealRequest); // URL determined elsewhere; can result in 422 error
q.await(function(error, file1, file2) {
  console.log(error)  // await never executes; this console never fires
  var rings = transformAPI(file2)
}

The browser simply logs "Failed to load resource: the server responded with a status of 422" but nowhere in defer() or await() am I able to catch this error.

How do multiple queues share time?

If I create two queues:

var queue4 = queue(4);
var fifo = queue(1);

and then put tasks into each randomly, have I effectively created 5 threads? When will each get control? Anything else I should be aware of or I am outside the flight envelope here?

Handle synchronous errors.

We should be explicit about how we handle synchronous errors.

  • An error could be thrown when starting a task (c.apply(null, t)).

The would prevent any other tasks from starting (since starting is never reset). If the task still manages to invoke its callback, an error would be thrown since tasks[i] would be undefined and the queue would think it was a duplicate callback; thus the queue would never complete. We should treat this as if the task invoked its callback with the error.

  • An error could be thrown when aborting a task (t.abort()).

This would prevents other active tasks from being aborted, and the await callback from being invoked. Since aborting the queue is treated as if a task threw an error, and since the queue only reports the first error that is thrown, we should ignore these errors and continue.

  • An error could be thrown when invoking the await callback.

The last one doesn’t appear to be an issue since the queue is already terminated by then. (However, note that an error here could bubble up to be an error in the above two situations.)

Consider adding queue.deferAll.

[Original subject: forEach-ish defer]

IMO adding queue.deferAll(array, method) to match .awaitAll would not harm this library's (greatly appreciated!) minimalism:

queue(N).deferAll(input, work).awaitAll(function (e, output) {
    …
});

vs.

var q = queue(N);
input.forEach(function (d) { q.defer(work, d); });
q.awaitAll(function (e, output) {
    …
});

The keyword 'await' is reserved

Trying to setup rollup to pull in d3, and i'm running into this error:

Error parsing ..../node_modules/d3-queue/src/queue.js: The keyword 'await' is reserved (92:20)

await/awaitAll called before pending tasks are complete after a call to queue.abort()

This behaviour isn't explicitly documented, I don't think, but it's not what I'd expect.

So, for instance:

var queue = require("d3-queue").queue;

function longRunningTask(done) {
    setTimeout(function () {
        console.log('task finished')
        done(null);
    }, 1000);
}

var q = queue(1)
    .defer(longRunningTask)
    .defer(longRunningTask)
    .await(function (error) {
        console.log('all done');
    });

setTimeout(function () {
    q.abort();
});

I would expect this to eventually log these lines:

task finished
all done

but it actually logs:

all done
task finished

In my use case, I cannot define abort functions on my tasks, in case you suggest that I just provide those.

Change parallelism during run time ?

Hello !

In my program, I'm using queue-async to queue up async commands for a SSH driver, as I need to do about 50 of them. However, I cannot do more than 20 at a time, so a standard for loop doesn't work.
The thing is, as everything is very dynamic with lots of async functions here and there, I need to check if the SSH connection has been made beforehand, and if it hasn't, I just queue it up first.

My issue is that by doing so, I have to start of with a parallelism of 1 (to make sure connection has been made), therefore all of my commands execute one by one, which is rather slow. Any ideas as to how speed up the thing ?

Draft/mock code to illustrate (using promises) :

var q = queue(1);
array.forEach( function (i) {
    asyncDatabaseQuery(i).then ( function (result) { 
                             if (!connected) q.defer(function (callback) { SSH.connect().then(callback); });
                             q.defer( function (callback) { asyncSSHCommand(result) });
});

bower

It would be nice to have this registered on bower, so it can be added to projects quickly and easily!

.awaitAll calling early

I'm defining a queue and adding .defer tasks using a loop followed by an .awaitAll, I'm having an issue where the .awaitAll is getting called before all of the tasks have been completed and I'm not sure where the issue lies.

var findLatLon = function(loc, callback){
   d3.json("http://maps.googleapis.com/maps/api/geocode/json?address=" + loc + "&sensor=true", 
      function(err, res){
         callback( res['results'][0]['geometry']['location'] )
      })
}

var q = d3.queue();

for (var i = 0; i < locationArray.length; i++){
   q.defer(findLatLon, locationName);
}

q.awaitAll(function(err, res) {
    if (err) throw err;
   console.log("queue done",  res);
});

running a bazillion asynchronous tasks does not work.

function test_promise(arg) {
    return Promise.resolve(arg).then(function(data) {
        console.log(arg, data); return data;
    });
}

var q = queue(5);
for (i=1; i<40; i++) { q.defer(test_promise, i); }
q.awaitAll(function(error, results) { console.log(error, results); })

here is the console output:

1 1 VM1308:2
2 2 VM1308:2
3 3 VM1308:2
4 4 VM1308:2
5 5 VM1308:2
Object {defer: function, await: function, awaitAll: function}

not sure why it does the first 5 calls but then stops.

Allow "defer after await" at any time before notification triggered

I realized today that some utility scripts I wrote are no longer working since the current version explicitly checks for and throws on defer after await. Reviewing when/why this might have gotten enforced it looks like I have a history of beating this drum already!

My use case boils down to essentially cases of "pagination" or "following linked lists asyncronously". I.e. the task I'm enqueuing does some work and only in the process of doing said work determines whether more work needs to be done. Thus my pattern is to create a queue, defer the initial task, and immediately awaitAll the eventual results.

My current workaround is to hack around the assertion:

const {queue:q} = require('d3-queue');

const _actualDefer = q.prototype.defer;
q.prototype.defer = function () {
  var _origCall = this._call;
  this._call = null;
  var result = _actualDefer.apply(this, arguments);
  this._call = _origCall;
  return result;
};

My proposal would be that defer can be called at any time before the notification actually happens. Likely implementation would be something like:

const TOMBSTONE = Symbol("await called"); // NOTE: `Object.create(null);` for older JS engines would suffice

function maybeNotify(q) {
  if (!q._active && q._call) {
    var d = q._data;
    q._data = undefined; // allow gc
    q._call(q._error, d);
    q._call = TOMBSTONE;
  }
}

and replacing

if (this._call) throw new Error("defer after await");

with

if (this._call === TOMBSTONE) throw new Error("defer after await called");`

I can submit a PR including any ancillary doc/test changes if you'd be amenable to this behavior change.

Uncaught TypeError: d3.queue is not a function D3v5 vanilla

Problem description and reproduction

The D3 library is loaded within <head> and in the main script log d3 object and call d3.queue.

Macos High Sierra 10.13.6 and Chrome 67.0.3396.99 (Official Build) (64-bit).

<head>
  <script src="https://d3js.org/d3.v5.js"></script>
</head>
document.addEventListener("DOMContentLoaded", () => {
  console.log(d3); 
  d3.queue();
}

Chrome console

Uncaught TypeError: d3.queue is not a function
    at HTMLDocument.document.addEventListener (main.js:15)

The full log including the d3 object is attached below, and I can't find queue. As the installation section stated,

You can also load directly from d3js.org, either as a standalone library or as part of D3 4.0. AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3 global is exported:

I am wondering. has the queue been removed from the D3 5.0 main library?

localhost-1533822745479.log

Consider adding queue.deferImmediate.

[Original subject: Syncronous queue.await triggering considered harmful]

In reference to the comment on #4, I would like to cast my vote against ever triggering the await/awaitAll callback synchronously.

While there is some discussion to the contrary, it is generally considered a bad practice to have this sort of inconsistency. One example of its harm is given under Keeping callbacks truly asynchronous.

Thinking of calling this library alone, the inconsistency doesn't seem so harmful — although there have been times I've felt my code could have been cleaner if I'd been able to define the queue and its error handling before starting to defer tasks.

However, if I want to use this library and still provide my callers with guaranteed asyncronocity, then the problem is more apparent:

function doThings(cb) {
    var q = queue(1).await(cb);
    if (maybe1) q.defer(thing1);
    if (maybe2) q.defer(thing2);
}

becomes

function doThings(cb) {
    var q = queue(1);
    if (maybe1) q.defer(thing1);
    if (maybe2) q.defer(thing2);
    if (maybe1 || maybe2) q.await(cb);
    else setTimeout(cb, 0);
}

Support callback functions with more than one result.

Some node operations return (err, result1, result2, ...). In this case the results after the first one are dropped.

For instance, if we have fs.read(..., function(err, bytesRead, buffer) {}), only bytesRead is returned.

I'm not sure what the remedy to this is. The easiest thing to do is have results return an array of arrays, but in the common case (fs.stat-like), this adds an unnecessary layer of indirection.

bind variable to .await()

Is it possible to pass a variable to .await()?
It doesn't look like .await() supports .bind(). Forgive my ignorance, I'm new to both d3 and asynchronous programming.

Here's my code for context
`function update_left_chart_v2() {
console.log("\n update left chart called v2!");
var leftSection = $("#dashboard > div:nth-child(2) > div.left-section").width();

for (svgSection in indexSVGSections) {
    //define variables for section
    sectionNumber = indexSVGSections[svgSection]["sectionNumber"];
    sectionMetric = indexSVGSections[svgSection]["sectionMetric"];
    sectionMetricCode = indexSVGSections[svgSection]["sectionMetricCode"];
    sectionIndexes = indexSVGSections[svgSection]["indexes"];
    console.log('section number: ' + sectionNumber + ' |section metric: ' + sectionMetric + '|section metric code: ' + sectionMetricCode + '\n');



    //add each d3.csv call to a queue, process when all completed
    var q = d3.queue();

    for (var index in sectionIndexes) {
        index = sectionIndexes[index];
        var fileName = index["fileName"];
        var indexActive = index["active"];
        var className = index["className"];

        if (indexActive) {
            console.log(index);
            var filePath = directory + "out-" + fileName + "-" + sectionMetricCode + ".csv";
            console.log(filePath);

            q.defer(d3.csv, filePath);

        } else {

            q.defer(nullIndex);
        }

    }

    function nullIndex(callback) {
        callback(null);
    }
    

    q.await(function(error, index1Data, index2Data, index3Data) {
       //THIS IS WHERE I NEED TO USE THE VARIABLE "sectionNumber" which changes with each iteration of for loop
       //
       console.log(sectionNumber);
        if (error) {
            console.log(error);
        } else {
            if(sectionCounter > 5){
                sectionCounter = 1;
            }

            console.log("await finished");
            console.log(" index 1 data");
            console.log(index1Data);

            // console.log("index 2 data");
            // console.log(index2Data);
            // console.log("\n");

            // console.log("index 3 data");
            // console.log(index3Data);
            // console.log("\n");

            var dataAssigned = false;
            index1Data.forEach(function(rowData, i) {
                rowDate = rowData.date;
                rowValue = rowData.value;
                if (rowDate.substr(0, 4) == sliderYear && !dataAssigned) {
                    console.log("date: ", rowDate, " | row value: ", rowValue, " | i: ", i);
                    dataAssigned = true;

                    // indexSVGSections[section1]

                }

            });
            console.log("\n");

        }
    });

}

}`

when "console.log(sectionNumber)" is called, it reads and calls it as its last assigned value, as it's being called asynchronusly. I need to bind each iteration of sectionNumber to .await() so it can read it as it changes.

Uncaught TypeError: Object #<Object> has no method 'apply'

Hi, thanks for you superb lib! I have a question though. Im trying to do an async call and fetch some data from the server. But i keep getting Uncaught TypeError: Object # has no method 'apply' and can't really work out what to do with it...

#this code works as expected when doing just one call to the server , 
# things start to get messy when need to loop through dep.client array 
# to perform a couple calls in a row 

  refresh_model: (some_param) =>
        params = {}
        params['some_param'] = some_param
        @model = new Model
        @model.fetch
              data: params
              success: =>
                console.log(@model)


    refresh_dependencies: (model) =>
        dep =
            client: [
                'some_param'
                'some_param'
            ]       
        self = @    
        q = queue(1)
        dep.client.forEach (t) ->
            q.defer(self.refresh_model(t))

Rewrite pop() to be non-recursive?

I'm a "long-time" user of Queue and think it's a very elegant framework to manage control over multiple callbacks.
I did two modifications which I wanted to share. I'm not sure of the pros and cons, but they seem to work.

The first one allows me to specify the "await" before any "defer" without risking premature zero results.

Line 33:
if (!remaining) await(error, results);
->
setTimeout(function() { if (!remaining) await(error, results) },0)

And the second change is to circumvent any potential Maximum Recursions by putting a similar timeout on the pop() in defer:
Line 26:
pop()
->
setTimeout(pop,0);

Support for promises.

You could implement a queue that takes promises rather than callbacks. And likewise you could make the queue itself a promise, rather than exposing queue.awaitAll. (As a promise, you’re limited to getting an array of results as the value rather than returning multiple values.)

You probably don’t need both callbacks and promises in the same queue… If you’re using promises, you could always promisify / denodeify your callbacks to wrap them in promises.

component.json error

component.json is broken

it needs a scripts entry, something like:

"scripts":["queue.js"]

otherwise, component install creates an empty directory with just the component.json file.

Errors thrown inside .await() are silenced

Errors thrown inside .await are silenced when concurrency is set to 1 and the last task in the queue isn't async.

var queue = require('d3-queue').queue;
var q = queue(1);

q.defer(function(callback) {
    process.nextTick(callback);
});

q.defer(function(callback) {
    callback();
});

q.await(function(err) {
    console.log('an error message and stack trace should follow:');
    throw new Error('this is an error');
});

I'd expect the output of this script to include a stack trace and the exit code to be 1. Instead, there is no output after the console.log and the exit code is 0.

Using node v4.5.0.

Support for indefinitely-long queues.

It’d be nice to create a queue simply to manage the invocation of tasks, without worry about collecting their results. This way, the queue wouldn’t grow in size based on the total number of tasks, but only on the number of pending and active tasks.

See Infinite Queue for an example (that eventually runs out of memory). But this isn’t a great example because it only defers a task when a previous task finishes, so it’d be pretty trivial to rewrite it as six independent serialized repeating tasks without needing a queue.

One possible API would be a queue.awaitNone method. Unlike queue.await and queue.awaitAll, you’d still be allowed to call queue.defer afterwards.

var q = d3.queue(1).awaitNone();

(function deferTask() {
  q.defer(task);
  setTimeout(deferTask, d3.randomExponential(1 / 60));
})();

But would queue.awaitNone take a callback that receives an error? Should an error prevent any future execution of tasks, like it does with a normal queue? (Is that what you want with an indefinitely-long queue?)

Support for tasks that depend on previous results.

Queue is intended to run a bunch of tasks concurrently and then invoke a callback when the tasks are all done, similar to Promise.all. Unfortunately, this means that Queue doesn’t really help you escape callback hell if each task depends on the previous task. For example, if you had this:

function complexTask(callback) {
  computeOne(function(error, one) {
    if (error) return void callback(error);
    computeTwo(one, function(error, two) {
      if (error) return void callback(error);
      computeThree(two, function(error, three) {
        if (error) return void callback(error);
        callback(null, three);
      });
    });
  });
}

Queue doesn’t really help you… I mean, you could do something like this, but it’s a hideous hack, and it’s so verbose I don’t think it qualifies as “helping”:

var one,
    two,
    three;

function complexTask(callback) {
  d3.queue(1)
      .defer(function(callback) {
        computeOne(function(error, result) {
          if (error) return void callback(error);
          one = result;
        });
      })
      .defer(function(callback) {
        computeTwo(one, function(error, result) {
          if (error) return void callback(error);
          two = result;
        });
      })
      .defer(function(callback) {
        computeThree(two, function(error, result) {
          if (error) return void callback(error);
          three = result;
        });
      })
      .await(function(error) {
        if (error) return void callback(error);
        callback(null, three)
      });
}

There was a nice suggestion in #20 of a clone of promise.then, which might look like this:

d3.then(function(callback) {
  d3.queue(1)
      .defer(getSettings)
      .defer(gitInit)
      .defer(gitRemoteOriginDoesNotExist)
      .defer(gitListUntrackedFiles)
      .await(callback);
}).then(function(settings, _, _, files, callback) {
  d3.queue(1)
      .defer(confirmFiles, files)
      .defer(gitAdd, files)
      .defer(gitCommit)
      .defer(createGist, settings.token)
      .await(callback);
}).then(function(_, _, _, id, next) {
  d3.queue(1)
      .defer(gitRemoteAdd, id)
      .defer(gitPush)
      .defer(openBrowser, id)
      .await(callback);
});

But, there are some related questions:

  • Does d3.then return a Promise? (Probably not, see below…)
  • Does d3.then take a onfulfilled handler and an optional onrejected handler like promise.then? Or does it take a single callback, which receives the error as the first argument? Or does d3.then return an object with a promise.catch-like function?
  • Does it allow the callback to receive multiple values? (Probably it must, based on the above example…)
  • Does d3.then return an object with an abort method? Does it then perhaps call abort on the return value of then function passed to d3.then? Is there a way to abort a then sequence like you can abort a queue?

d3_queue Uncaught Error

Hi, i had a working setup with d3 queue. For some reason it does not work any more and i have no clue after two days. I updated to the latest version. Exactly the same behaviour. To some extent i believe it has nothing to do with d3_queue itself. It just happens to it.

The code bails on first use of defer. Nothing is done by defer, it seems to fail immediately. If i am going in the browser console and instantiate a new instance it works.

(http://uhvauerr.de/ --> login with defaults --> select any marker --> error)

var lade = function(rahmen, cback){
        var url = "/api/v2/datad3?rahmen=" + rahmen + "&geraet=" + geraet;

        d3.json(url).header('X-Access-Token', token).header('X-Key', user).get(function (error, data) {
            cback(error, data);
        });
    };

    var taskqueue = d3_queue.queue(2);
    var frameurl = "/api/v2/frames?geraet=" + geraet;

    d3.json(frameurl).header('X-Access-Token', token).header('X-Key', user).get(function(error, data){
        $.each(data, function (index, frame) {
            console.log(frame.rahmen);
            taskqueue.defer(lade, frame.rahmen); //<<<<<<<<<<<<<<<<<<<<<<<<ERROR HERE
        });
    });

    taskqueue.awaitAll(function(error, results) {

        //console.log(results);
        var data = [];

        results.forEach(function (c, i, a) {
            console.log(c);
            //c.knoten_id;
            c.datensaetze.forEach(function (d, j, a) {
                d.key = "" + (c.knoten_id) + "_" + d.key; // WORKAROUND Rahmen id sollte vom json file kommen
                data.push(d);
            });
        });

        console.log(data);

        if(data.length != 0) {
            makeGraph(data);
        }
    });
Uncaught Error
c.defer @ d3-queue.min.js:1
(anonymous function) @ VM484:135
n.extend.each @ jquery-2.2.0.min.js:2
(anonymous function) @ VM484:133
(anonymous function) @ d3.js:1899
event @ d3.js:434
respond @ d3.js:1852

Unhandled rejection RangeError: Maximum call stack size exceeded

I'm getting the following error. My jobs are all calling done() correctly.

Feb 18 16:22:02 analytics-migration app/analytics_migration.1: Unhandled rejection RangeError: Maximum call stack size exceeded
Feb 18 16:22:02 analytics-migration app/analytics_migration.1: at notify (/app/node_modules/queue-async/queue.js:47:18)

The problem may be the scale I'm using. My queue size is 180,000 items, and I'm using 256-way concurrency. Is that likely to cause stack overflows?

Bower Package missing build?

Hi there,
I'm using queue-async as both a bower component and node module, and I'm noticing a discrepancy between the packages that my webpack config is having a hard time resolving. The node module seems to be pre-built, having a build folder pre-existing when you pull the repository down -- however the bower component seems to be only available through an es6 loader. Was this intentional, or is the bower component missing a build script?

Consider adding queue.deferWith.

[Original subject: Apply queue as context to deferred function call]

Right now the library does f.apply(null,a) — it would be nice if the context could be overridden e.g. queue.deferUsing(ctx, fn, …) or if the context were set to the queue itself.

Actually, nix queue.deferUsing — that's what Function.prototype.bind is for. But passing the queue instead of nothing seems useful, no?

Adding individual callbacks

I needed a simple way to handle multiple downloads and this seems perfect. However, attempting to display a progress bar or similar would necessitate a callback when a task is completed. The current callback is not accessible by the user, which is fair, but maybe you could allow the user to inject his own callback (a little like async would do)?

I've fiddled with it and I think I have a minor addition to the library. It takes the last argument of the defer call, and if it's a function, calls it in the current callback. It takes the result of the task as well as the number of remaining tasks as parameters (so you can do stuff if you want).

The only problem I suppose would be that you can't have a function as the last argument anymore, unless you wanted it to be called at the end of the task. But if you want to use that function inside of the request, then it's not possible anymore. You can still pass functions, but it can't be the last.
Ex:

var c = function(r, remaining) { console.log(remaining) };
var f = function(foo) { console.log(foo) };
var q = queue()
   .defer(request, 1, c) // c called when request is done
   .defer(request, f, c) // you can use f inside request if you want
   .defer(request, foo, f) // f would be taken as the callback -- can break some existing code
   .awaitAll(ready);

I will submit a pull request and if it fits the library's spirit I'd be happy to see it integrated. I can submit a couple of tests as well as the minified version, should it be ok for integration.

Throw errors for illegal states.

  • Calling queue.defer after queue.await or queue.awaitAll.
  • Calling queue.await after queue.await or queue.awaitAll.
  • Calling queue.awaitAll after queue.await or queue.awaitAll.
  • Receiving more than one callback from a task.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.