NOTICE: This is probably not a duplicate since I have a much different requisite, and I couldn't find the solution yet.
Here's the scenario:
I'm using a custom component which has a getSignature function.
About this function:
It must be synchronous, adding async key word will not work.
This function will be automatically called while the component is mount.
This function must returns some data for further use.
But now the signature must be generated synchronously(from an HTTP request), and can't be generated before this component is actually mount.
Here's the simplified code:
class Showcase extends UniqueComponent {
getSignature() {
function runAsyncSynchronously(asyncFunc) {
// Is this possible?
}
function getData() {
return new Promise(resolve => {
setTimeout(() => resolve(42), 3000);
});
}
let data = runAsyncSynchronously(getData);
return data; // output "42" after 3 seconds
}
}
Is this possible to make this happen?
Doesn't matter if the whole process is frozen while waiting.
I tried this but it just got stuck forever...
function runAsyncSynchronously(asyncFunc) {
let isDone = false;
let result = null;
asyncFunc().then(data => {
isDone = true;
result = data;
});
while (!isDone){
}
return result;
}
Related
I'm desperately trying to recover the value of a callback function but I have no idea how to do that. I have a function where I execute this code:
if (final.error !== undefined) {
console.log("Initial authentication:", final.error_description, "Please refresh the authentication grant");
extAuthCallback(84);
} else {
tokens.set('access_token', final.access_token)
.set('expires_in', final.expires_in)
.set('refresh_token', final.refresh_token)
.set('refresh_date', moment())
.write()
extAuthCallback(1);
}
});
Who performs this function:
function extAuthCallback(result) {
return result;
}
And which is called by this variable:
let authentication = auth.extAuth(access_token, auth.extAuthCallback);
I would like my `authentication' variable to take the value returned in the callback, and I have no idea how to do that. Returning the callback function to my original function doesn't work.
You could use a promise, would need to use an async function as well though.
function asyncExtAuth(access_token) {
return new Promise(resolve => {
auth.extAuth(access_token, resolve);
});
}
let authentication = await asyncExtAuth(access_token);
Lets say I have the following function:
let x = 1
function countForever() {
setTimeout(function() {
console.log(x)
x = x +1
countForever()
});
}
We also have an object which contains an EventEmitter called e. e has a state, and if that state doesn't equal 3, we wish to kill our function. We can achieve this with the following:
let x = 1
function countForever() {
if (e.state != 3) return
setTimeout(function() {
console.log(x)
x = x +1
countForever()
});
}
This works. However my real, non example function has a lot more steps in it, and I've found myself continually if checking the state, 8-10x through the function.
Given e is an EventEmitter I would like to catch these changes when the state changes and kill the function. Luckily, e already has an event we can listen for:
e.on('state_changed' , function(new_state) {
// Kill countForever
})
How do I stop execution of this function from outside of its scope?
EDIT: I don't know why I wrote a sample function with a setTimeout, it seems I've been quite misleading. Here's a better one:
async function functionToKill() {
if (e.state != 3) return
thing1 = await functionThatTakesALongTime()
if (e.state != 3) return
thing2 = await secondFunctionThatTakesALongTime()
if (e.state != 3) return
thing3 = await thirdFunctionThatTakesALongTime()
//.....
if (e.state != 3) return
thing10 = await tenthFunctionThatTakesALongTime()
}
// e is an event emitter
e.on('state_changed' , function(new_state) {
// Kill/interrupt functionToKill
})
Effectively within the function I'm continually checking for the state over and over and returning if it's changed. I don't feel this is clean, and would like to do the equivalent of a return from an external call triggered by an eventEmitter
You don't give us a whole lot to go on in your sample function, but here's an idea with that code. You use a master promise that when rejected causes your sequence of await operations to abort:
// create deferred object so it can be rejected externally
Promise.Deferred = function() {
if (!(this instanceof Promise.Deferred)) {
return new Promise.Deferred();
}
let p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = this.promise.then.bind(p);
this.catch = this.promise.catch.bind(p);
if (this.promise.finally) {
this.finally = this.promise.finally.bind(p);
}
}
// shared promise, when rejected our function stops advancing to more operations
let killPromise = new Promise.Deferred();
function raceKill(p) {
return Promise.race(killPromise, p);
}
async function functionToKill() {
try {
thing1 = await raceKill(functionThatTakesALongTime());
thing2 = await raceKill(secondFunctionThatTakesALongTime());
thing3 = await raceKill(thirdFunctionThatTakesALongTime());
//.....
thing10 = await raceKill(tenthFunctionThatTakesALongTime());
} catch(e) {
// perhaps handle kill errors separately from actual function rejection errors
}
}
// e is an event emitter
e.on('state_changed' , function(new_state) {
// Kill/interrupt functionToKill
killPromise.reject(new Error("state_changed"));
})
This structure with Promise.race() has a bit of an advantage in that it doesn't even wait for functionThatTakesALongTime() to finish before aborting (when your pseudo-code would have been able to check e.state). It aborts immediately when you reject killPromise. That other asynchronous operation isn't magically cancelled. It will still do what it was going to do, but your functionToKill() won't wait around for it.
With actual code, there are probably more elegant ways to do this than using shared scope variables like killPromise, passing parameters, sharing something as object properties, etc... But, hopefully this shows you the general idea.
kill countForever? you can save the return value of setTimeout function with a variable such as timer, then clearTimeout(timer) when state_changed event fired. I don`t know if what I understand is right?
This is a simple version of what I'm trying to do in my application. I have an if statement which evaluates the result of a function call and then populates an array if the statement comes back as true. AFTER the if statement is completely finished, I want to run some more code such as the console.log as seen below.
I understand that the if's evaluation is taking too long to finish and javascript just continues to the console.log because of its asynchronicity. How do I make the code wait for the if statement to complete?
var tabs = [];
if (isTrue()) {
tabs.push('some string');
}
console.log(tabs[1]);
function isTrue() {
setTimeout(function() {
return true;
}, 500)
}
You can just wrap your code in a Promise and consume the returned values by calling then on it:
var tabs = [];
isTrue().then(res => {
if (res) {
tabs.push('some string');
}
return tabs;
}).then(arr => {
console.log(arr);
});
function isTrue() {
//Just wrap your existing code in a Promise constructor
return new Promise((resolve, reject) => {
setTimeout(() => {
//Pass whatever value you want to consume later to resolve
resolve(true);
}, 500)
});
}
You could pass a callback to the isTrue() function, something like:
function isTrue(_callback) {
setTimeout(function() {
// code here
// Call the callback when done
if (typeof(_callback) === 'function')
_callback(tabs);
});
}
function showTabs(tabs) {
console.log(tabs[1]);
}
isTrue(showTabs);
Ought to work.
Using modern javascript, you can achieve that using promises and async/await:
const isTrue = () => new Promise(resolve => setTimeout(resolve, 500, true));
// you can only use `await` inside an `async` function
async function main() {
// better use `let` instead of `var` since `let` is block scoped,
// see:
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let>
let tabs = [];
if (await isTrue()) {
tabs.push('some string');
}
// array's index start by 0, not 1
console.log(tabs[0]);
}
main();
(this code also use arrow functions for isTrue.)
isTrue() returns undefined. The return true inside of the setTimeout callback will return back to the timeout call, not to the isTrue() call. The code executes immeadiately and there is no asynchronity involved (except for that timer that does nothing).
I have an async function that I want to finish if an event doesn't happen in a few seconds. I've already accomplished that with a timeout. The code is something like this:
fn: async function (inputs, exits) {
let finished = false;
let ws = await sails.helpers.createWebSocketConnection();
ws.on('message', async function onMessage(data) {
finished = true;
return exits.success(ws);
});
// If it's taking too long, return undefined
setTimeout(function () {
if (!finished) {
return exits.success();
}
}, 3000);
}
As you can see, I have a boolean to avoid calling the callback twice, but it doesn't seems like the correct way to do it. Is there a better way to know if exits.success() was already called in Sails?
I want to fetch data and have it ready for another function to use as a javaScript object. The problem is that the data is fetched after the program completes. Here is the link to the project: https://github.com/bigbassroller/isomorphic-js/blob/master/src/components/pages/Home/HomeController.js. See code here:
import "babel-polyfill";
import Controller from '../../../lib/controller';
import nunjucks from 'nunjucks';
import fetch from "isomorphic-fetch";
import promise from "es6-promise";
function onClick(e) {
console.log(e.currentTarget);
}
function getData(context) {
let data = {
"name": "Leanne Graham"
}
return data;
}
function fetchData(context) {
return fetch("http://jsonplaceholder.typicode.com/users/1").then(function(response) {
let data = response.json().body;
return data;
});
}
export default class HomeController extends Controller {
index(application, request, reply, callback) {
this.context.cookie.set('random', '_' + (Math.floor(Math.random() * 1000) + 1), { path: '/' });
this.context.data = { random: Math.floor(Math.random() * 1000) + 1 };
callback(null);
}
toString(callback) {
// Works
let context = getData(this.context);
// Doesn't work
// let context = fetchData(this.context);
context.data = this.context.data;
nunjucks.render('components/pages/Home/home.html', context, (err, html) => {
if (err) {
return callback(err, null);
}
callback(null, html);
});
}
attach(el) {
console.log(this.context.data.random);
this.clickHandler = el.addEventListener('click', onClick, false);
}
detach(el) {
el.removeEventListener('click', onClick, false);
}
}
Is it possible to have the data fetched before the the page renders? I am trying to keep things as vanilla as possible, because I am trying to learn as much as possible. I've been stuck for days trying to solve this problem, so I am coming to SO for help, and to help others who have the same problem.
My issue is similar to this issue, https://github.com/reactjs/redux/issues/99 but I am not trying to use redux, would rather use promises instead.
When using async calls you can't have a guarantee of when the call will return (therefore async). Which means that if you want something done after the data is returned the place to do it is inside the "then" clause.
Could you please explain some more on your usecase here?
It is not possible. You'd need to change your program design to work with this. Here is a simple example:
Suppose you have some function foo() that returns a string:
function foo() {
x = fetchSync();
return x;
}
Now suppose you don't have fetchSync() and you're forced to do the work asynchronously to compute the string to return. It is no longer possible for your function to have the string ready to return before the end of the function is reached.
So how do you fix it? You redesign the foo() function to be asynchronous too.
function foo(callback) {
// kick off fetch
fetch(function(response) {
// call callback() with the
// the results when fetch is done
callback(response.json())
});
}
Same example using Promises:
function foo() {
return fetch().then(function(response) {
return response.json();
});
}
Generally most environments that run JavaScript will support asynchronous designs. For example, in Node, a JavaScript program will not finish running if there is callbacks registered that can still be called.