Question as below:
Create a function GoodMan
GoodMan("Tom")
output: I am Tom
GoodMan("Tom").rest(10).learn("computer")
output: I am Tom
//wait 10 seconds
Start learning after 10 seconds
Learning computer
GoodMan("Tom").restFirst(5).learn("english")
output:
//wait 5 seconds
Start learning after 5 seconds
I am Tom
Learning english
I have finished the first two questions. However, in the third question, how to execute the restFirst(5) function firstly in chain functions in Javascript?
Should I add a selector to determine whether GoodMan("Tom") execute.
Code shows below:
var GoodMan = function(str){
var callback_f = new Object;
str1 = "I am " + str;
console.log(str1);
callback_f.rest = function(num){
sleep(num * 1000);
console.log ("Start learning after " + num + " seconds");
return callback_f;
};
callback_f.learn = function(str){
str2 = "Leaning " + str;
console.log (str2);
};
return callback_f;
}
function sleep(ms) {
var unixtime_ms = new Date().getTime();
while(new Date().getTime() < unixtime_ms + ms) {}
}
GoodMan("Tom").rest(10).learn("computer");
How about having a queue of tasks?
function TaskQueue(tasks) {
return function createQueue(...args) {
const queue = [];
let running = false;
async function processQueue() {
if(running) return;
running = true;
// console.log(queue);
for(const { task, args } of queue) await task(...args);
running = false;
}
function addTask(task, priority, args) {
queue[priority ? "unshift" : "push"]({ task, args });
setTimeout(processQueue);
}
const instance = {};
for(const [key, priority, task] of tasks) {
instance[key] = function(...args) { addTask(task, priority, args); return this; };
}
if(instance.constructor) instance.constructor(...args);
return instance;
};
}
const GoodMan = TaskQueue([
["constructor", 0, (name) => console.log(`Hi, ${name}`)],
["rest", 0, (time) => new Promise(res => setTimeout(res, time * 1000))],
["restFirst", 1, (time) => new Promise(res => setTimeout(res, time * 1000))],
["learn", 0, subject => console.log(`Learning ${subject}`)]
]);
GoodMan("Tom").restFirst(5).learn("english")
You can try the code below:
const goodMan = (name) => {
return Promise.resolve(`I am ${name}`);
};
const restFirst = (time) => {
return Promise.resolve(`Start learning after ${time} seconds`);
};
const learn = (subject) => {
return Promise.resolve(`Learning ${subject}`);
};
const allTogether = (async () => {
const goodManOutput = await goodMan('Tom');
const restOutput = await restFirst(5);
const learnOutput = await learn('English');
return Promise.resolve(`${restOutput} ${goodManOutput} ${learnOutput}`);
});
allTogether().then(newOutput => {
console.log(newOutput);
});
Hope this helps.
Related
Hello there I am getting the desired result when am using promise but how should I implement it with callback when am returning something from the function.
result with promise
const first = () => {
return ("I am first");
}
const second = () => {
return new Promise((resolve,reject) => {
setTimeout(()=>{
resolve("I am second");
},1000);
})
}
const third = () => {
return("I am third");
}
//using promise
const solve = async () => {
var f = first();
console.log(f);
var ans = await second();
console.log(ans);
var t = third();
console.log(t);
}
solve();
*** using callback ***
const first = () => {
return "I am first";
}
var temp;
const second = (cb) => {
setTimeout(function() {
return "I am second";
temp = cb();
}, 1000);
}
const third = () => {
return "I am third";
}
const solve = () => {
var f = first();
console.log(f);
var s = second(third);
setTimeout(()=>{
console.log(s);
console.log(temp);
},1100)
}
solve();
OUTPUT should be
I am first
I am second
I am third
You don't need that global temp variable, and your setTimeout callback in second doesn't really work. It should be cb("I am second");, just like where you'd usually call resolve("I am second"); in a new Promise. Then you can receive that value as the parameter of the callback function you're passing to second(…), which should then log it and continue with the remaining steps of the script.
const first = () => {
return "I am first";
}
const second = (cb) => {
setTimeout(function() {
cb("I am second");
}, 1000);
}
const third = () => {
return "I am third";
}
const solve = () => {
var f = first();
console.log(f);
second((s) => {
console.log(s);
const t = third();
console.log(t);
});
}
solve();
Notice this is not unlike your promise version if you were to use .then() instead of async/await syntax:
const solve = () => {
var f = first();
console.log(f);
second().then((s) => {
console.log(s);
var t = third();
console.log(t);
});
}
So basically im working on a cron job in my app that fires every 3 hours and updating users 'score' by calling the RiotApi
basically the function so far
exports.updatePlayersPoints = async () => {
console.log('STARTED UPDATING');
try {
const players = await UserLadder.findAll();
await Promise.all(
players.map(async (player) => {
const p = await RiotAccount.findOne({
where: {
userId: player.userId,
},
include: RiotRegions,
});
const beginTime = new Date(player.dataValues.createdAt);
let data;
try {
const res = await axios.get(
`https://${
p.dataValues.riot_region.dataValues.name
}.api.riotgames.com/lol/match/v4/matchlists/by-account/${
p.dataValues.accountId
}?queue=420&beginTime=${beginTime.getTime()}&api_key=${
process.env.RIOT_KEY
}`
);
data = res.data;
} catch (error) {
if (!error.response.status === 404) {
console.error(error);
}
}
if (!data) {
return;
}
let totalScore = player.dataValues.userPoints;
await Promise.all(
data.matches.map(async (match, i) => {
if (i < 15) {
const { data } = await axios.get(
`https://${p.dataValues.riot_region.dataValues.name}.api.riotgames.com/lol/match/v4/matches/${match.gameId}?api_key=${process.env.RIOT_KEY}`
);
const calculateScore = () => {
return new Promise((resolve) => {
const { stats } = _.find(
data.participants,
(o) => o.championId === match.champion
);
const killsPts = stats.kills * 2;
const deathPts = stats.deaths * -1.5;
const assistsPts = stats.assists;
const wardsPts = stats.wardsPlaced / 4;
const firstBloodPts = stats.firstBloodKill ? 3 : 0;
const firstBloodAssistPts = stats.firstBloodAssist ? 3 : 0;
const firstTowerPts = stats.firstTowerKill ? 2 : 0;
const firstTowerAssistPts = stats.firstTowerAssist ? 2 : 0;
const score =
killsPts +
deathPts +
assistsPts +
wardsPts +
firstBloodPts +
firstBloodAssistPts +
firstTowerPts +
firstTowerAssistPts;
totalScore += score;
resolve();
});
};
await calculateScore();
}
})
);
const user = await UserLadder.findOne({
where: {
userId: player.userId,
},
});
user.userPoints = parseFloat(totalScore);
user.lastGameId = data.matches[0].gameId;
await user.save();
})
);
console.log('FINISHED UPDATING');
} catch (error) {
console.error(error);
}
};
Basically it just looks up the table userladder to find the players that are signed to the ladder and for each one of these players it fires a map function that makes a request to the riotapi to get the match history of this player and then later make an inside map function to map each one of these matches.
but basically I updated it to now keep track of the game id of the last call before 3 hours so it doesn't have to make request that was already done.
user.lastGameId = data.matches[0].gameId;
but now in my second map function that maps the matches I wasn't it so that if the last game from my database matches the game id that currently being mapped I want to stop the map function and not continue this record or the ones after because it also means they all have been already counted.
but I can not seem to find a way to do it.
i tried using break; but it didn't work
any ideas?
using for loop
I tried a small test with for loop so I tried
for (let i = 0; i < 15; i++) {
await new Promise(async (resolve, reject) => {
const match = data.matches[i];
console.log(match);
resolve();
if (i === 1) {
break;
}
});
}
but I still go the same error
SyntaxError: Illegal break statement
Instead of trying to "break" a map, you should filter the matches that you want to process before you execute the map.
Something like this:
await Promise.all(
const filteredMatches = data.matches.filter(match => match.gameId > previousId);
filteredMatches.map(async (match, i) => { ...
More on filter() in javascript.
Edit: If generated id's are random and are not ordered, you can store all previous id's in a Set, and then just ask if it has been previously added
await Promise.all(
const filteredMatches = data.matches.filter(match => mySet.has(match.gameId));
filteredMatches.map(async (match, i) => { ...
More on Set in javascript.
I am trying to implement two classes that can deal with asynchronous tasks in JavaScript:
Class Task: mimics the execution of a task with setTimeout. Once the timer expires, the task is considered completed.
Class TaskManager: has a capacity parameter to limit the numbers of tasks that can be executing in parallel.
I thought if I could just call the loop function recursively, just to keep checking if one job is done, I could proceed to the next job. But this leads immediately to a "Maximum call stack size exceeded" error.
Can someone explain how I can fix this?
class Task {
constructor(time) {
this.time = time;
this.running = 0;
}
run(limit, jobs, index) {
setTimeout(() => {
console.log('hello', index);
this.done(limit, jobs, index);
}, this.time);
}
done(limit, jobs, index) {
jobs.splice(index, 1);
console.log(jobs);
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.jobs = [];
this.index = 0;
this.running = 0;
this.pending = [];
}
push(tk) {
this.jobs.push(tk);
this.index += 1;
const loop = () => {
if (this.jobs.length === 0) {
return;
}
if (this.jobs.length <= this.capacity) {
this.running += 1;
tk.run(this.capacity, this.jobs, this.index-1);
return;
}
loop();
}
loop();
}
}
const task = new Task(100);
const task1 = new Task(200);
const task2 = new Task(400);
const task3 = new Task(5000);
const task4 = new Task(6000);
const manager = new TaskManager(3);
manager.push(task);
manager.push(task1);
manager.push(task2);
manager.push(task3);
manager.push(task4);
You should not implement the busy loop, as that will block the event loop and so no user UI events or setTimeout events will be processed.
Instead respond to asynchronous events.
If you let the setTimeout callback resolve a Promise, it is not so hard to do.
I modified your script quite drastically. Here is the result:
class Task {
constructor(id, time) {
this.id = id;
this.time = time;
}
run() {
console.log(this + ' launched.');
return new Promise(resolve => {
setTimeout(() => {
console.log(this + ' completed.');
resolve();
}, this.time);
});
}
toString() {
return `Task ${this.id}[${this.time}ms]`;
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.waiting = [];
this.running = [];
}
push(tk) {
this.waiting.push(tk);
if (this.running.length < this.capacity) {
this.next();
} else {
console.log(tk + ' put on hold.');
}
}
next() {
const task = this.waiting.shift();
if (!task) {
if (!this.running.length) {
console.log("All done.");
}
return; // No new tasks
}
this.running.push(task);
const runningTask = task.run();
console.log("Currently running: " + this.running);
runningTask.then(() => {
this.running = this.running.filter(t => t !== task);
console.log("Currently running: " + this.running);
this.next();
});
}
}
const a = new Task('A', 100);
const b = new Task('B', 200);
const c = new Task('C', 400);
const d = new Task('D', 5000);
const e = new Task('E', 6000);
const manager = new TaskManager(3);
manager.push(a);
manager.push(b);
manager.push(c);
manager.push(d);
manager.push(e);
Not an entirely serious question, more of a shower thought: JavaScript's await keyword should allow for something that feels an awful lot like a mutex in your average "concurrent language".
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
// Lock
await mutex.lock();
// Unlock
mutex.unlock();
Is this a correct implementation (apart from proper error handling)? And… can I have C++-RAII-style lock guards?
Your implementation allows as many consumers obtain the lock as ask for it; each call to lock waits on a single promise:
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
const mutex = new Mutex();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("A got the lock");
})();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("B got the lock");
})();
You'd need to implement a queue of promises, creating a new one for each lock request.
Side notes:
new Promise(t => t()) can be more simply and idiomatically written Promise.resolve() :-)
No need for self if you're using arrow functions like that; arrow functions close over the this where they're created (exactly like closing over a variable)
Probably would make sense for unlock to be a resolution value of the lock promise, so only the code that obtained the lock can release it
Something like this:
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
Live Example:
"use strict";
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
const rand = max => Math.floor(Math.random() * max);
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
const mutex = new Mutex();
function go(name) {
(async () => {
console.log(name + " random initial delay");
await delay(rand(50));
console.log(name + " requesting lock");
const unlock = await mutex.lock();
console.log(name + " got lock");
await delay(rand(1000));
console.log(name + " releasing lock");
unlock();
})();
}
go("A");
go("B");
go("C");
go("D");
.as-console-wrapper {
max-height: 100% !important;
}
Is this a correct implementation?
No. If two tasks (I can't say "threads") try to do mutex.lock() while it is currently locked, they will both get the lock at the same time. I doubt that's what you want.
A mutex in JS is really just a boolean flag - you check it, you set it when you acquire a lock, you clear it when you release the lock. There's no special handling of race conditions between checking and acquiring as you can do it synchronously in single-threaded JS, without any other threads interfering.
What you seem to be looking for however is a queue, i.e. something where you can schedule yourself to get the lock and will be notified (through a promise) when the previous lock is released.
I'd do that with
class Mutex {
constructor() {
this._lock = null;
}
isLocked() {
return this._lock != null;
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return () => {
if (this._lock == lock) this._lock = null;
release();
};
}
acquireSync() {
if (this.isLocked()) throw new Error("still locked!");
return this._acquire();
}
acquireQueued() {
const q = Promise.resolve(this._lock).then(() => release);
const release = this._acquire(); // reserves the lock already, but it doesn't count
return q; // as acquired until the caller gets access to `release` through `q`
}
}
Demo:
class Mutex {
constructor() {
this._lock = Promise.resolve();
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return release;
}
acquireQueued() {
const q = this._lock.then(() => release);
const release = this._acquire();
return q;
}
}
const delay = t => new Promise(resolve => setTimeout(resolve, t));
const mutex = new Mutex();
async function go(name) {
await delay(Math.random() * 500);
console.log(name + " requests lock");
const release = await mutex.acquireQueued();
console.log(name + " acquires lock");
await delay(Math.random() * 1000);
release()
console.log(name + " releases lock");
}
go("A");
go("B");
go("C");
go("D");
I recommend using a library like async-mutex:
const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}
I've got the following problem:
I have a page evaluation in puppeteer which includes asnychronous parts. I want to return the value from the asynchronous part to puppeteer, however, it just returns undefined without waiting for the Promise to resolve. Does anybody how to solve the problem?
My Sample code:
const puppeteer = require('puppeteer');
async function testing(num) {
const browser = await puppeteer.launch({
headless: false,
ignoreHTTPSErrors: true
});
const page = await browser.newPage();
const evaluating = await page.evaluate((num) => {
//some synchrnous stuff (declaring some variablesand so on...)
function lookForNumber(num) {
if (num > 2) {
var asyncstuff = setTimeout(function () {
if (num > 10) {
console.log('number is greater than 9');
var whatIwantToRetrun = 'ten';
return Promise.resolve(whatIwantToRetrun);
//return here
}
if (num > 5 && num < 10) {
console.log('number is samller than 10');
var whatIwantToRetrun = 'nine';
return Promise.resolve(whatIwantToRetrun);
//return here
}
else {
num++;
lookForNumber(num)
}
}, 2000);
}
}
lookForNumber(num)
}, num)
console.log(evaluating); // returns undefined before function has finished
}
testing(4)
Puppeteers example:
const result = await page.evaluate(() => {
return Promise.resolve(8 * 7);
});
console.log(result); // prints "56"
Chromes API on evaluate
According to this link and the updated API, puppeteer always evaluates the code, and, if the evaluation is a promise, waits for the promise to resolve and returns the promise value.
Thanks in advance for any help!
Edit: I figured it out!
The solution to my problem:
const puppeteer = require('puppeteer');
let date = require('date-and-time');
async function testing(num) {
const browser = await puppeteer.launch({
headless: true,
ignoreHTTPSErrors: true
});
const page = await browser.newPage();
await console.log('starting evaluation');
let now = new Date();
let time = date.format(now, 'YYYY/MM/DD HH:mm:ss');
console.log(time);
const test = await page.evaluate(async (num) => {
console.log('starting evaluation');
//some synchrnous stuff (declaring some variablesand so on...)
function lookForNumber(num) {
return new Promise((resolve, reject) => {
if (num > 2) {
var asyncstuff = setTimeout(function () {
if (num > 10) {
console.log('number is greater than 9');
var whatIwantToReturn = 'ten';
resolve(whatIwantToReturn);
}
if (num > 5 && num < 10) {
console.log('number is samller than 10');
var whatIwantToReturn = 'nine';
resolve(whatIwantToReturn);
}
else {
num++;
lookForNumber(num)
}
}, 5000);
}
});
}
var returnvalue = await lookForNumber(num);
return returnvalue;
}, num)
console.log(test);
now = new Date();
time = date.format(now, 'YYYY/MM/DD HH:mm:ss');
console.log(time);
await browser.close();
}
testing(6)