Plugin Action (context.async) will not execute more than once

I have a plugin action …

As you can see the function returns asynchronously and does a console log. Unfortunately the issue is that I perform this action one time and it works, but when I try to run it again it the code runs (the console.log(‘run’) is executed … ) but the context.async does not.

I’m not sure why that’s the case. What am I missing here? Any help would be appreciated :slight_smile:

1 Like

I have this exact same problem while using context.async in plugin element actions.
I managed to run context.async several times on client-side actions only, with the exact same code.
I believe this issue deserves a bug report.

1 Like

Really? Damn … So I need to use client-side actions I guess. :frowning:

There are some mismatched design patterns in your use of promises and asynchronous functions:

  1. Unless you need to extract a value from a promise or asynchronous function, or want to catch the promise rejection of a server side function in the server logs there is no need to wrap your asynchronous code in acontext.async.
  2. You are passing the (undefined) result of calling console.log to the error handling part of the callback. This is problematic and will result in unpredictable behaviour.
  3. Ditto for your promise result, you are passing the (undefined) result of console.log to a eventually a return statement. Again more unpredictable behaviour.
  4. Pick either return Promise or async function for your design pattern, not both. I personally prefer the older promise chaining as it forces me, as a developer, to think explicitly about asynchronous dependencies and orders of operation.

I would refactor your code to stick to one design pattern. For example using promise chaining:

function(instance, properties, context) {

    // Start (writes first)
    console.log("Start");

    // Process (writes third)
    instance.data.whiteboard.board.get()
    .then((s) => { console.log(s); })
    .catch((r) => { console.log(r); });

     // End (writes second)
     console.log("End")
}

Note: there are browser and Node.js dependent idiosyncrasies when parsing anonymous functions. Some are more tolerant of partial syntax than others. I always use fully “curly brace” anonymous syntax to guard against parser errors.

Server-side pattern using context.async to mimic await behaviour:

const mypromise = makeapromise();
const myresolved = context.async(
    (callback) => {
        mypromise
        .then((result) => { callback(null, result); })
        .catch((reason) => { callback(reason); });
    }
);

Note: You can chain as many thens and Promise.alls or Promise.anys together outside of context.async before finally extracting a value with context.async

2 Likes

Still the same.
Only one execution for plugin’s element action possible.
So it seems impossible to async set elements states???

I am having the same problem, has anyone found out how we can get around this?

Only solution is to use a client side action by passing the instance via a global variable or via saving it to the html element.

Just for fun, you can try this in your element action:

if (instance.data.n === undefined) {
	instance.data.n = 0;
}
const myFN = async (cb) => {
	try {
		const value = await doAsyncStuff();
		instance.publishState("value", value);
		instance.triggerEvent("done");
		cb(null, value);
	} catch (e) {
		cb(e);
	}
};

myFN.toString = () => {
	return instance.data.n;
};

const value = context.async(myFN);

instance.data.n++;

and it will run the async function every thime you trigger the action.

Of course it is an undocumented hack,
it can probably break other internal functionality, it will probably not work once bubble changes the plugin async api.

Do not use it in production.

You gotta live on the edge man… :sunglasses:

use it in pro :wink:

2 Likes

If you’re talking about element plugins, you can just use async/await syntax if you’re not loading any data. (If loading data, you’ll have to split your action into two parts, the first being the function that is called by the action code wherein you load your lists or things or whatnot and then calls an async function where you can await what needs to be awaited.)

1 Like

Did anyone find a real solution? The workaround from @dorilama works, but not sure if that would be the best way to fix this.

I reached out to Bubble support and they kinda admitted that context.async for Plugin Element Actions gets (wrongly) cached:

Bubble Support recommended to try out context.v3.async but that does not apply here as this is about Plugin Element Actions.

The next and final message from Bubble support stated that they do not offer support for 'custom code (‘we’re unable to assist you further with custom code’).

This is a very basic reproducible example: Bubble | No-code apps

Would be amazing if anyone has made this work, or if we can confirm that @dorilama’s workaround will not break in the future (CC: @grace.hong, @nick.carroll, @allenyang ) :slight_smile:

nobody can confirm that that hack will not break in the future, that’s why you shouldn’t use it :upside_down_face:
when I wrote that post it was working, but it was a funny excercise and I never used it in production.