1

I'm currently working on a function that takes a pretty long time to finish and since I won't be able to make it finish faster and im going to call it from other Scripts I was wondering if there is a method to use something like a promise in that function.

Basically

function longrunning(){
   var def = new $.Deferred();
   var result = {};
   [DO STUFF THAT TAKES A WHILE]
   def.resolve();
   return def.promise(result);
}

My basic problem is, that since all the stuff thats going on isn't async my promise won't be returned until everything is done, so the function that will later be calling longrunning won't know it's async. But of course if I return the promise before executing all of the code, it won't resolve at all. I hope you're getting what I'm trying to do. Hope someone has an idea. Thanks in advance and

Greetings Chris

3
  • 1
    What is it that the function does? Specifically, does it manipulate the DOM? Commented Feb 16, 2017 at 14:47
  • No, your function will not get faster because you are using promises. The only thing that could help is to chunk the longrunning thing into little pieces, so that your whole program stays reactive (while taking longer for the thing). Commented Feb 16, 2017 at 15:07
  • The function basically is reordering a big json object, that was retrieved from another software. The DOM is not beeing manipulated. The code won't be faster but the site would not be frozen while this stuff is being calculated Commented Feb 20, 2017 at 8:08

1 Answer 1

1

Wrapping the code in a $.Deferred (or native promise) won't help even if you do manage to get the promise back to the calling code before doing the long-running work (for instance, via setTimeout). All it would accomplish is making the main UI thread seize up later, soon after longrunning returned the promise, instead of when calling longrunning itself. So, not useful. :-)

If the function in question doesn't manipulate the DOM, or if the manipulations it does can be separated from the long-running logic, this is a good candidate to be moved to a web worker (specification, MDN), so it doesn't get run on the main UI thread at all, but instead gets run in a parallel worker thread, leaving the UI free to continue to be responsive.

longrunning wouldn't do the actual work, it would just postMessage the worker to ask it to do the work, and then resolve your promise when it gets back a message that the work is done. Something along these lines (this is just a code sketch, not a fully-implemented solution):

var pendingWork = {};
var workId = 0;
var worker = new Worker("path/to/web/worker.js");
worker.addEventListener("message", function(e) {
    // Worker has finished some work, resolve the Deferred
    var d = pendingWork[e.data.id];
    if (!d) {
        console.error("Got result for work ID " + e.data.id + " but no pending entry for it was found.");
    } else {
        if (e.data.success) {
            d.resolve(e.data.result);
        } else {
            d.reject(e.data.error);
        }
        delete pendingWork[e.data.id];
    }
});

function longrunning(info) {
    // Get an identifier for this work
    var id = ++workid;
    var d = pendingWork[id] = $.Deferred();
    worker.postMessage({id: id, info: info});
    return d.promise();
}

(That assumes what the worker sends back is an object with the properties id [the work ID], success [flag], and either result [the result] or error [the error].)

As you can see, there we have longrunning send the work to the worker and return a promise for it; when the worker sends the work back, a listener resolves the Deferred.

If the long-running task does need to do DOM manipulation as part of its work, it could post the necessary information back to the main script to have it do those manipulations on its behalf as necessary. The viability of that naturally depends on what the code is doing.


Naturally, you could use native promises rather than jQuery's $.Deferred, if you only have to run on up-to-date browsers (or include a polyfill):

var pendingWork = {};
var workId = 0;
var worker = new Worker("path/to/web/worker.js");
worker.addEventListener("message", function(e) {
    // Worker has finished some work, resolve the Deferred
    var work = pendingWork[e.data.id];
    if (!work) {
        console.error("Got result for work ID " + e.data.id + " but no pending entry for it was found.");
    } else {
        if (e.data.success) {
            work.resolve(e.data.result);
        } else {
            work.reject(e.data.error);
        }
        delete pendingWork[e.data.id];
    }
});

function longrunning(info) {
    return new Promise(function(resolve, reject) {
        // Get an identifier for this work
        var id = ++workid;
        pendingWork[id] = {resolve: resolve, reject: reject};
        worker.postMessage({id: id, info: info});
    });
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks alot. You're right, I didn't really think of a webworker before

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.