1

I'm currently struggling to make an interop call work because I can't synchronously wait for a Promise in javascript.
Before you think about closing this as duplicate, note that I have done some searching and I understand that it says everywhere it's not possible to synchronously wait for a Promise. Once async, always async. Source(s): How to wait for a JavaScript Promise to resolve before resuming function?, How can I synchronously determine a JavaScript Promise's state?, Call An Asynchronous Javascript Function Synchronously, Can i force a promise to resolve synchronously (reddit).
I've seen this generator approach but I have not been able to implement it correctly, so maybe there's some potential left.
For now synchronously waiting for the Promise doesn't seem possible. Now I'd like to know what I can do instead in my situation.

Note: I think I should mention that I don't know javascript all too well.

In my case I cannot rethink my design entirely because I don't control the consumer nor the the actual interop-call. The only thing I have full control over is the object I hand to the consumer.

Here's a basic example of my issue (jsfiddle, code below).
I have a filter function that accepts or rejects an index. That function is a member of a config object which gets passed to the consumer (I have full control over the config object). The consumer uses this filter later on to determine if an index should be filtered out or not. I have no control over the consumer and the consumer may use the filter at any time without me having an influence on that.
Now if I just use a normal non-async function in my config object, everything works great. But I'd like to forward my call to C#, use a filter there and return the result to the consumer. I'm using blazor and the interop call returns a promise (there's also a synchronous method but that doesn't work for all blazor variants) so I have to await it to get the value. To await something I have to make my function async and that means it will also return a Promise. Now I'm in this once async, always asnyc. The consumer just checks if the value returned by the filter is truthy and because a Promise is an object, it will be truthy no matter what it will resolve to.

var normalFilter = index => index % 2 == 0;
var asyncFilter = async index => {
    // same filter but returns after 500ms. In the actual application the result is returned from a c# function
    return await fakeInteropCall(index, normalFilter);
}

var conf = {
    filter: normalFilter    // if you put asyncFilter here, everything will be filtered
};

fakeConsumer(conf);         // call the consumer with the config. Note that in the actual application, the consumer just stores the filter and uses it later. I can't influence when the filter gets executed.

// I can't change this consumer, it's actually chart.js
function fakeConsumer(config) {
    for (let i = 0; i < 5; i++) {
        if (config.filter(i)) {
            console.log("filtered " + i);
        } else {
            console.log(i);
        }
    }
}

// I can't change this interop call, it's from blazor and only the async version works on all variants
function fakeInteropCall(index, filterFunc) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(filterFunc(index));
        }, 500);
    });
}

What can I do to ensure that this consumer gets the correct values?
The last resort would be to use the non-async interop call and only support half the blazor variants but that would be very unfortunate.

Joelius
  • 3,839
  • 1
  • 16
  • 36
  • If the consumer isn't promise-aware, you'll need to resolve the promise *then* call the consumer with the value. As all of your references tell you, you can't synchronise an async operation. – jonrsharpe Dec 31 '19 at 12:43
  • @jonrsharpe I should've mentioned that in the text but as you can read in the code-comment, I can't do that because I just register it. The consumer will use my filter as callback for a render event, I don't actually influence _when_ my function will be called, I just provide it. – Joelius Dec 31 '19 at 12:55
  • "*Now I'm in this once async, always asnyc.*" - looks like you already understood it. If the consumer expects a synchronous return value, you cannot do anything asynchronous. "*there's also a synchronous method*" - then you should just use that. – Bergi Dec 31 '19 at 13:14
  • The alternative is to change your entire design: compute the filtering asynchronously for all indices before even starting chart.js, store the results in a data structure and just synchronously look it up when chart.js calls the filter function. – Bergi Dec 31 '19 at 13:17
  • @Bergi I can't do the second one because the example I showed is quite simple with a single filter function but in the real world example it has to work for all callbacks regardless of arguments (done with ...args) and return type (js is (too) flexible). This means I don't know what arguments the callback expects and what it returns, I just delegate it to C# and am done with it. I think the only solution is to determine if the callback has a return value and if yes, use synchronous and throw an exception on server-side where that's not supported and use async for the rest (just not awaited). – Joelius Jan 01 '20 at 13:16

0 Answers0