0

I'm looking to see if there is a way of taking a generic async function, such as AJAX, or web worker on message, and forcing it to behave synchronously.

First off - I really do need this behaviour, I am aware of all the arguments regarding async callbacks vs returns - but in this case, I truly need to wait for a callback.

If there is no generic solution available I'd settle for one that works purely on the Worker.prototype.onmessage as that is the primary use case here.

I'm fairly sure the answer will be "thats not possible" but I wanted to check before surrendering.

I've already looked into promises and generators, I'd prefer not to use a third party library (I'm aware of fiber/futures for Node).

A potential use case:

function main(){
    worker = new Worker("someWorker.js")
    var result = worker.onmessage(function(event){
        return event.data;
    })
    //wait for worker.onmessage
    return result;
}
Zack Newsham
  • 2,810
  • 1
  • 23
  • 43
  • Related jQuery question: http://stackoverflow.com/q/133310/901048 – Blazemonger Jun 10 '16 at 16:16
  • @Blazemonger, thanks - but this will only work for ajax calls. Definately worth noting though – Zack Newsham Jun 10 '16 at 16:33
  • Simply start the worker and put all the synchronous code, which depends on the workers result, in `onmessage` callback. Promises and generators just solve the same problem with more elegance. – Rudolf Gröhling Jun 10 '16 at 16:35
  • @Vaclav, as I mentioned - I really need the return, the project I'm working on is not feasible to rewrite all code into callbacks – Zack Newsham Jun 10 '16 at 16:45

1 Answers1

-1

If you want to preserve synchronous flow of function, the generator function is the only possible solution. You'll send out all async tasks like var result = yield new Worker('someWorker.js').

Outside the function* main you'll get the worker with mainFn.next().value and then you attach onmessage listener which will return resolved data into function* main and the function will be resumed.

The complete generator example looks like this

// write function as generator
function* main () {
    var result = yield new Worker("someWorker.js");
    return result;
}

// instantiate the generator and get the first yield value
var mainFn = main()
  , worker = mainFn.next().value

// set onmessage listener, which is callback, that sends data back to *main()
worker.onmessage = function(event) {
    mainFn.next(event.data);
}

This way your main() function will remain almost similar to original form, so you can reuse the original code, but you need to write surrounding logic, which wouldn't be simple and which would use callbacks anyway.

You seem to be aware what generators are and that it's not widely supported in the browsers wild, so if you want run this code on web page, you'll need ES6 transpiler Babel.

Rudolf Gröhling
  • 4,611
  • 4
  • 27
  • 37
  • This doesnt work, because the subsequent calls to `mainFn.next()` do not wait for the `worker.onmessage` – Zack Newsham Jun 13 '16 at 14:11
  • @ZackNewsham first call to `next()` yields `Worker` out from the `main()`, second call to `next()` passes `event.data` back to `main()`, which substitutes `Worker` with `event.data` and resumes `main()`. Second call to `mainFn.next()` is placed inside callback, so it waits for message event for sure, but it is possible, that your worker returns multiple messages. In that case, you want to accumulate messages to some variable and then after the data are complete, pass it back to `main()` with `mainFn.next(completeData)`. – Rudolf Gröhling Jun 14 '16 at 10:57
  • I tried with a trivial worker, it could be that generator is implemented differently in different browsers (I'm using Chrome). Additionally, I'm using this in Meteor, which compiles code before deployment - so it could be messing with things. – Zack Newsham Jun 14 '16 at 13:41
  • took me a while to wrap my head around it - it does work, but sadly isnt much cleaner than compiling the code into callbacks - as I'd have to restructure the code to use generators instead, I'd hoped I could simply call the generator, and have it handle all the next, etc - then return the final result. – Zack Newsham Jun 14 '16 at 19:28
  • @Zack Newsham the tricky part of generators is, that they are asynchronous to the outside world. You would be able to more or less use your old code, but you need to wrap it in quite complicated processing logic, which will resolve all yielded values and also the final `return`, which works like yield too (beware that `result` is not yielded from [`for ... of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of)) and this async result you then pass to other generator etc, so eventually you may realize, that complete rewrite into callbacks isn't such a bad idea. – Rudolf Gröhling Jun 15 '16 at 18:57