I want to call a function from a method. I don't even know how to ask correctly. test.onload() is called immediately, not after 3 seconds. See code for example, please.
export default class {
constructor() {
// some code
setTimeout(() => {
this.onload;
}, 3000);
}
onload = (fn) => {
console.log('loaded event');
fn();
};
}
const test = new TEST();
test.onload(function () {
console.log('from instance');
});
You are calling the function directly. Somehow you think that will be called when the setTimeout runs which is not the case.
If you want the function "from instance" to be called you need to rethink how you are registering it. You are going to have to store the function somehow and let the timer pick it up and execute it.
Setting it with the constructor
class TEST {
constructor(callback) {
this.callback = callback;
setTimeout(() => this.onload(), 3000);
}
onload = () => {
console.log('loaded event');
if(this.callback) this.callback();
};
}
const test = new TEST(function () {
console.log('from instance');
});
Setting it with a method
class TEST {
constructor() {
setTimeout(() => this.onload(), 3000);
}
onload = () => {
console.log('loaded event');
if(this.callback) this.callback();
};
registerCallback(callback) {
this.callback = callback;
}
}
const test = new TEST();
test.registerCallback(function () {
console.log('from instance');
});
Related
The example below shows a snippet of my application. The assumption is: some functions are executed after the document is loaded, others only after the POST response. On this momet, callbacks are placed in the array and executed after the request is completed. It all works as expected. I would like to change these callbacks to one Promise which would be fired just like now "Update.endRequest()"; I wonder if it is possible? Any suggestions are welcome. Or an idea if it can be done better.
update.js
class UpdateClass {
constructor() {
this.callbacks = [];
}
success(callback) {
this.callbacks.push(callback);
}
endRequest() {
this.callbacks.forEach(callback => callback());
}
}
const Update = new UpdateClass();
export default Update;
file1.js
import Update from './update';
export default class File1Class {
constructor () {
this.init();
}
init() {
Update.success(this.update);
exampleFunction();
}
update() {
exampleFunctionAfterUpdate();
}
}
function exampleFunction() {
console.log('On document load 1');
}
function exampleFunctionAfterUpdate() {
console.log('After Update 1');
}
file2.js
import Update from './update';
export default class File2Class {
constructor () {
this.init();
}
init() {
Update.success(this.update);
anotherExampleFunction();
}
update() {
anotherExampleFunctionAfterUpdate();
}
}
function anotherExampleFunction() {
console.log('On document load 2');
}
function anotherExampleFunctionAfterUpdate() {
console.log('After Update 2');
}
app.js
import Update from './update';
import File1Class from './file1';
import File2Class from './file2';
class AppClass {
constructor () {
this.init();
}
init() {
new File1Class();
new File2Class();
triggerfunction();
}
}
function triggerfunction() {
const button = document.getElementById('trigger');
button.addEventListener('click', () => {
xhr.open('POST', 'someUrl', true);
xhr.onload = () => {
if (xhr.status === 200) {
Update.endRequest();
}
};
xhr.send(someJsonData);
});
}
window.app = new AppClass();
You can use something like this
const promiseObj = new Promise((resolve,reject)=>{
exampleFunctionAfterUpdate();
anotherExampleFunctionAfterUpdate();
resolve(true);
});
function triggerfunction() {
const button = document.getElementById('trigger');
button.addEventListener('click', () => {
xhr.open('POST', 'someUrl', true);
xhr.onload = () => {
if (xhr.status === 200) {
// Update.endRequest();
promiseObj.then(()=> console.log('Functions executed'));
}
};
xhr.send(someJsonData);
});
}
I pass the callback to setTimeout and internally I need to determine who owns this callback. For example:
class Test1 {
func1() {
console.log('test1');
}
complete() {
console.log('complete');
}
}
function test2() {
console.log('test2');
}
function timeout(callback, delay) {
setTimeout(() => {
callback();
callback.getOwner()?.complete();
}, delay);
}
In this case, I need to return the owner of this callback in order to execute the complete () function.
let obj = new Test1();
timeout(obj.func1.bind(obj));
timeout(test2);
I understand that complete() can be done inside func1(), like this:
class Test1 {
func1() {
console.log('test1');
this.complete();
}
complete() {
console.log('complete');
}
}
But this is just a small example.
Why all methods in chain executed at the same time? I wrap method in setTimeout. What I did wrong?
I know how to do this with promises but I'm learning chaining technique.
const delay = function delay(func) {
return function(...args) {
setTimeout(() => {
return func.apply(this, [...args]);
}, 2000);
return this;
};
}
class TimePopup {
constructor() {
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
The issue you have is that delay does not schedule things to run relative to each other. Each thing is delayed exactly the time it's called, so new TimePopup().firstMethod(1).secondMethod(2).thirdMethod(3); will call all three methods at the same time and each is delayed the same amount of time relative to the call time. Then all three fire after two seconds.
Instead, you need to process them sequentially. Similar to this answer of mine, you can use create a promise queue to handle the sequence. Each method adds to the chain instead and there is a delay between before it's executed:
const wait = ms => () =>
new Promise(resolve => setTimeout(resolve, ms));
let queue = Promise.resolve();
function delay(func) {
return function(...args) {
queue = queue
.then(wait(2000))
.then(() => func.apply(this, [...args]));
return this;
};
}
class TimePopup {
constructor() {
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
A slightly more reusable approach is to create the delay functions on demand, so you can have different queues for different tasks. This can look like this:
const delayMaker = delayBetween => {
let queue = Promise.resolve();
return function delay(func) {
return function(...args) {
queue = queue
.then(wait(delayBetween))
.then(() => func.apply(this, [...args]));
return this;
};
}
}
/* ... */
const delay = delayMaker(2000);
const wait = ms => () =>
new Promise(resolve => setTimeout(resolve, ms));
const delayMaker = delayBetween => {
let queue = Promise.resolve();
return function delay(func) {
return function(...args) {
queue = queue
.then(wait(delayBetween))
.then(() => func.apply(this, [...args]));
return this;
};
}
}
class TimePopup {
constructor() {
const delay = delayMaker(2000);
this.firstMethod = delay(this.firstMethod);
this.secondMethod = delay(this.secondMethod);
this.thirdMethod = delay(this.thirdMethod);
console.log('This is the constructor');
}
firstMethod(val) {
console.log('This is the First Method', val);
}
secondMethod(val) {
console.log('This is the Second Method', val);
}
thirdMethod(val) {
console.log('This is the Third Method', val);
}
}
new TimePopup()
.firstMethod(1)
.secondMethod(2)
.thirdMethod(3);
In the pre-promise days one popular approach was to pass around a next function which is supposed to shift the next queued task from the stack and run it. A simplified example:
class App {
funcs = [];
use(func) {
this.funcs.push(func)
return this
}
next() {
if (this.funcs.length)
this.funcs.shift()(this.next.bind(this))
}
}
//
function delay(func) {
return function (next) {
setTimeout(() => {
func();
next();
}, 1000);
}
}
//
(new App())
.use(delay(() => console.log(1)))
.use(delay(() => console.log(2)))
.use(delay(() => console.log(3)))
.next()
i have a react functional component.
Here is i have getData function, with this function i'm getting some data from api and then if data have i wan't to call anotherFunc();, But i wan't to do that after 3 second. I tried to put there setTimeout function but it's worked directly not wait 3 second. What i need to do?
function Hello(props) {
anotherFunc = () => {
console.log('hello');
}
const getData = () => {
let data = {
id: 1
}
axios.post(baseUrl, data).then(res => {
if (res.data) {
setTimeout(() => {
anotherFunc();
}, 3000);
}
});
}
return <h1>Something</h1>;
}
export default Hello;
=================== Progress Update ===================
I've made my first Async function according to Mozilla and another tutorial. However, it couldn't prevent the event as I expected. It goes stacking when I click multiple times before the whole code is done.
My expectation is using async and promise to disable the event until the entire code is done which is the way that I use callback like this CodePen example.
Additionally, I can't fully understand the concept of Async and Promise. I think the Async + Promise function separates the code as itself such as the bookmark of the book in real life? It's really hard to understand what's going on inside of the code.
Can somebody explain to me that how async and promise work in the code and prevent the event?
This is a result that I've done so far:
class Async {
constructor(elem) {
this.elem = document.querySelectorAll(elem)
this.flag = true;
this.selector(this.elem, 'click');
}
selector(node, eventName) {
node.forEach(item => {
item.addEventListener(eventName, (e) => this.group(e))
})
}
waiting() {
if (this.flag) {
this.flag = false;
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('waiting . . .'))
}, 2000)
})
}
}
result() {
console.log('test');
this.flag = true;
}
async group(e) {
const a = await this.waiting();
const b = await this.result();
}
}
const async = new Async('.button');
=================== Original Question ===================
I'm separating the code blocks with import/export in Node js for looking clearer when I refactoring it.
A problem is boolean flag called this.flag doesn't prevent the event overriding when I pass it to init.js as a parameter like this:
See the code first:
// =================== init ===================
'use strict';
import Terminal from './terminal.js';
import Touch from './touch.js';
export const slider = (function() {
class Slider {
constructor(elem) {
this.elem = document.querySelector(elem);
this.flag = false;
this.terminal = new Terminal(this.elem);
this.touch = new Touch(this.elem);
this.terminal.insert(this.elem, 'click', this.touch.clicked.bind(this.touch));
}
wait(flag, callback) {
flag = false; // the boolean can't prevent the event overriding right now.
let bound = callback.bind(this);
console.log('waiting . . .');
setTimeout(bound, 1000, flag);
}
done(flag) {
console.log('done');
flag = true;
}
}
return {
init: new Slider('.contents')
}
}())
// =================== terminal.js ===================
export default class Terminal {
constructor(elem) {
this.elem = elem;
}
insert(node, eventName, callback) {
node.addEventListener(eventName, callback);
}
}
// =================== touch.js ===================
import {slider} from './a.js';
export default class Touch {
constructor(elem) {
this.elem = elem;
this.flag = true;
}
clicked(e) {
if (this.flag) {
console.log(`clicked`);
let slide = slider;
slide.init.wait(this.flag, slide.init.done);
}
}
}
But the weird thing is, when I replace the both functions wait() and result() to touch.js, it prevents the event until the countdown is done.
// touch.js
wait(callback) {
this.flag = false;
let bound = callback.bind(this);
console.log('waiting . . .');
setTimeout(bound, 1000);
}
done(flag) {
console.log('done');
this.flag = true;
}
I would like to know why the flag can't prevent the event when it passed to the another js file and how to make it disables the event temporarily.
Explanation
Generally speaking
The async/await pattern got introduced with ES2017 of JavaScript and is syntactic sugar for the older Promise's
Some older browsers don't support async/await
async is a newer way of saying return Promise((resolve) => { ... }).
await is the counterpart to async and is the newer way of saying .then(result => { ... }).
await can only be used in functions marked as async
try/catch is the counterpart to .catch(error => { ... }). It is not actually new, but you can use it in this context.
You can read more about async/await here
Code
I have made some minor changes to your code, so that it makes more sense and written some comments, so that you understand everything that is happening here.
class Async {
constructor(elem) {
this.elem = document.querySelectorAll(elem)
this.isRunning = false; // <-- Rename the flag variable to something more meaningful
this.selector(this.elem, 'click');
}
selector(node, eventName) {
node.forEach(item => {
item.addEventListener(eventName, (e) => this.group(e))
})
}
waiting() {
return new Promise((resolve, reject) => { // <-- Move the Promise to here so that every codepath returns something
if (!this.isRunning) {
this.isRunning = true;
console.log('Waiting ... '); // <-- Move the waiting before the timeout, because inside it is not actually waiting, its rather done
setTimeout(() => { // <-- setTimeout runs the provided function after the provided time in milliseconds elapsed
this.isRunning = false; // <-- Switch the isRunning after the timeout, because that makes more sense (because now it is not running anymore)
resolve('Done'); // <-- Change the text to done and actually resolve it (eg. remove the console.log)
}, 2000)
} else {
reject('There is already a button function running'); // <-- reject is like throwing an error
}
})
}
result() {
console.log('test');
}
async group(e) {
try {
const a = await this.waiting(); // <-- Assigns 'Done' to the variable a
console.log(a); // <-- prints the message
this.result(); // <-- prints 'test' immidiatly after the above console.log
} catch (error) {
console.log(error); // <-- Prints the reject message in case the button is already running
}
/* group could also be written with the old syntax like this:
this.waiting().then(result => {
console.log(result); // Will print "Done" after 2000 milliseconds
this.result(); // Will print test instantly after the above console.log(). You dont need to await it, because it is not an async function
}).catch(error => {
console.log(error); // Will print the reject message in case the button is already running
});
*/
}
}
const asyncButton = new Async('.button'); // <-- Don't use async as a variable name. It's a reserved keyword
Running Example
It is the same code again, but without comments, so that you can test it directly here on StackOverflow.
class Async {
constructor(elem) {
this.elem = document.querySelectorAll(elem)
this.isRunning = false;
this.selector(this.elem, 'click');
}
selector(node, eventName) {
node.forEach(item => {
item.addEventListener(eventName, (e) => this.group(e))
})
}
waiting() {
return new Promise((resolve, reject) => {
if (!this.isRunning) {
this.isRunning = true;
console.log('Waiting ... ');
setTimeout(() => {
this.isRunning = false;
resolve('Done');
}, 2000)
} else {
reject('There is already a button function running');
}
})
}
result() {
console.log('test');
}
async group(e) {
try {
const a = await this.waiting();
console.log(a);
this.result();
} catch(error) {
console.log(error);
}
}
}
const asyncButton = new Async('.button');
<button class="button">Test</button>
Got a solution. Adding await functions into the boolean flag is working.
class Async {
constructor(elem) {
this.elem = document.querySelectorAll(elem)
this.flag = true;
this.selector(this.elem, 'click');
}
selector(node, eventName) {
node.forEach(item => {
item.addEventListener(eventName, (e) => this.group(e))
})
}
waiting() {
return new Promise(resolve => {
setTimeout(() => {
resolve(console.log('waiting . . .'))
}, 1000)
})
}
result() {
console.log('test');
this.flag = true;
}
async group(e) {
// console.log(this.flag);
if (this.flag) {
this.flag = false;
console.log('test');
const a = await this.waiting();
const b = await this.result();
}
}
}
const async = new Async('.button');