Waiting for a set of workers to finish - javascript

I have an array of webworkers, called workers. I'm initiating them all in a single function, called activate. The problem is, I want to have the activate return the values that are posted by the worker. I either want it to return a promise of some kind or wait until they are all done.
So the code could be:
// the web workers add stuff in this array with onmessage()
var globalArray = [];
function activate(){
for(var i = 0; i < workers.length; i++){
workers[i].postMessage('do something');
}
return // Promise or filled globalArray;
}
So I could use it like this:
var values = await activate();
I don't want the workers to call a seperate function once the last worker is finished. Is there any way I can achieve this?

What you want to do is to create the Promise, and inside of the function of the Promise, initiate all the workers and check when the last ends to call the resolve function of the promise, and return this promise in your activate function.
Would be something like this:
// the web workers add stuff in this array with onmessage()
var globalArray = [];
function activate(){
var promise = new Promise(function(resolve, reject){
var counter = 0;
var array = [];
var callback = function(message){
counter++;
//You can add here the values of the messages
globalArray.push(message.data);
//Or add them to an array in the function of the Promise
array.push(message.data);
//And when all workers ends, resolve the promise
if(counter >= workers.length){
//We resolve the promise with the array of results.
resolve(array);
}
}
for(var i = 0; i < workers.length; i++){
workers[i].onmessage = callback;
workers[i].postMessage('do something');
}
});
return promise;
}
The code has not been tested for now, but hope you get the idea.

one way to do this is to wrap everything in a promise,
const workers = [ new Worker("./worker1.js"), new Worker("./worker2.js")];
const activate = () => {
return new Promise((resolve,reject) => {
let result = [];
for ( let i = 0 ; i < workers.length; i++) {
workers[i].postMessage("do something");
workers[i].onmessage = function(e) {
result.push(e.data);
};
}
resolve(result)
});
};
async function f() {
let res = await activate();
console.log(res);
}
f();

Related

Call Promise when setTimeout function is complete

I'm trying to implement Promise statements in my code and would love some help. I have a function that runs with a setTimeout. I want to call a function once that is completed.
Tried including Promise statements, but I have a feeling I'm not doing it correctly. Any feedback is helpful
function App(){
let exampleProm = new Promise(
function(){
type.type("hello , dexter", 100);
}
).then(console.log('finished'));
}
App();
//code that gets called first
module.exports = {
type: function(phrase, delaySpeed){
let total = 0;
let empty = [];
for(let i = 0; i < phrase.length;i++){
total += delaySpeed;
setTimeout(() => {
empty.push(phrase.charAt(i));
process.stdout.write(chalk.blue.bold(empty[i]));
if(empty.length === phrase.length){ //if complete
process.stdout.write('\n'); //puts on separate line
}
},total);
}
}
}
Use an array of promises that each resolve() inside the setTimeout() and then Promise.all() to run the code after they have all resolved
module.exports = {
type: function(phrase, delaySpeed) {
let total = 0;
let empty = [];
let promises = []
for (let i = 0; i < phrase.length; i++) {
total += delaySpeed;
// new promise for each character
let promise = new Promise(function(resolve, reject) {
setTimeout(() => {
empty.push(phrase.charAt(i));
process.stdout.write(chalk.blue.bold(empty[i]));
if (empty.length === phrase.length) { //if complete
process.stdout.write('\n'); //puts on separate line
}
// assuming above writes are synchronous can now resolve promise
resolve()
}, total);
});
// push new promise to array
promises.push(promise)
}
// return the all() promise
return Promise.all(promises)// add another then() if you need to return something to next then() in App()
}
}
function App(){
type.type("hello , dexter", 100).then(function(){
// this then() fires when the Promise.all() resolves
console.log('finished')
});
}
The reason your .then handler never gets called is because the promise is stuck in its initial state, which is "pending".
Promises can be pending or settled (fulfilled / rejected). The callback passed to a Promise constructor takes two params, resolve (trigger a fulfill) and reject (trigger a rejection).
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});
promise1.then(function(value) {
console.log(value);
// expected output: "foo"
});
Check out the source for the code above at the MDN docs for the Promise API.
I reckon the example above gives you a hint on how to implement promise based delays, but if you need further assistance, let me know.

Call function after Promise.all inside a loop

So I wrote javascript about promise. I made two promise inside a for loop like this:
for(let i=0; i<globalName.length; i++ ){
let debug = globalName[i];
var promise1 = new Promise(function(resolve,reject){
var j = searchStart(startT,debug);
resolve(j)
}).then(function(result){
sxx = result;
});
var promise2 = new Promise(function(resolve,reject){
var k = searchEnd(endT,debug);
resolve(k);
}).then (function(result){
syy = result;
});
Promise.all([promise1, promise2]).then(function(values) {
let localed = [];
entry[i] = sxx;
exit[i] = syy;
localed.push({
"name" : debug,
"first" : entry[i],
"last" : exit[i]
});
xtable.rows.add(localed).draw();
});
}
In each promise, I call function searchStart(startT,debug) and searchEnd(endT,debug), which within each function, I also wrote promise script that return value from an API (ready called API from a device, when I called it, returns JSON data). JSON data works fine, and I can access it with my function and returned some intended value.
With the Promise.all when my function returns value, I write the data into table provided from DataTables. But of course because the function run when two promises above resolved, it can only write to my table with every each row of data.
Now, what I want to ask is, can I somehow manage to write all data first, and after the data is complete I call other function to write to table?
You can .map each debug to its associated Promise.all, so that you have an array of Promise.alls. Then, after calling Promise.all on that array, you can add all rows at once.
Note that since searchStart and searchEnd look to already return Promises, there's no need for the explicit Promise constructor antipattern - simply use the existing Promise alone. Also, by returning a value inside a .then, you can avoid having to use outer variables like sxx, syy, entry[i], and exit[i]:
const promiseAlls = globalName.map((debug, i) => {
return Promise.all([
debug, // see below for note
searchStart(startT, debug),
searchEnd(endT, debug)
]);
});
Promise.all(promiseAlls).then((allArrs) => {
allArrs.forEach(([
name, // this is the same as the "debug" variable above
first, // this is the same as `entry[i]`, or `sxx`, in your original code
last // this is the same as `exit[i]`, or `syy`, in your original code
]) => {
const localed = [{ name, first, last }];
xtable.rows.add(localed).draw();
});
});
The debug is used in the initial Promise.all even though it's not a Promise so that it can be passed along and used with its other associated values, once they've been resolved.
I am not clear about what you want, but I have two answers which may help you
Solution 1: It will solve each promise at a time then proceed for next
function searchStartAndEnd(flag = false, date, debug){
return new Promise((resolve, reject)=>{
var j;
if(flag){
j = searchStart(date, debug);
}else{
j = searchStart(date, debug);
}
resolve(j)
})
}
for(let i=0; i<globalName.length; i++ ){
let debug = globalName[i];
sxx = await searchStartAndEnd(true, startT, debug);
syy = await searchStartAndEnd(false, endT, debug) ;
localed.push({
"name" : debug,
"first" : sxx,
"last" : syy]
});
xtable.rows.add(localed).draw();
}
solution 2: It will solve all promise parallel then do move to the next task, then move next iteration
function searchStartAndEnd(flag = false, date, debug){
return new Promise((resolve, reject)=>{
var j;
if(flag){
j = searchStart(date, debug);
}else{
j = searchStart(date, debug);
}
resolve(j)
})
}
for(let i=0; i<globalName.length; i++ ){
let debug = globalName[i];
sxx = await ;
[sxx, syy] = await Promise.all([searchStartAndEnd(true, startT, debug),
searchStartAndEnd(false, endT, debug)])
localed.push({
"name" : debug,
"first" : sxx,
"last" : syy]
});
xtable.rows.add(localed).draw();
}

Deferred Promise - Resend same request if callback doesn't meet criteria

I am trying to achieve the following functionality:
execute call back
resolve promise
check output
if not correct execute again
I have 'mimicked' the scenario with a timer, this reruns a script that makes a call to backend database for some information:
_runCheckScript: function(bStart, bPreScript){
var oController = this;
var scriptTimerdeferred = $.Deferred();
var promise = scriptTimerdeferred.promise();
if(typeof(bStart) === "undefined"){
bStart = true;
}
if(typeof(bPreScript) === "undefined"){
bPreScript = true;
}
// if the HANA DB is not stopped or started, i.e. it is still starting up or shutting down
// check the status again every x number of seconds as per the function
var msTime = 10000;
if(!bPreScript){
this._pushTextIntoConsoleModel("output", {"text":"The instance will be 'pinged' every " + msTime/1000 + " seconds for 2 minutes to monitor for status changes. After this, the script will be terminated."});
}
if(bPreScript){
var timesRun = 0;
var commandTimer = setInterval( function () {
timesRun += 1;
if(timesRun === 12){
scriptTimerdeferred.reject();
clearInterval(commandTimer);
}
// send the deferred to the next function so it can be resolved when finished
oController._checkScript(scriptTimerdeferred, bStart, bPreScript);
}, msTime);
}
return $.Deferred(function() {
var dbcheckDeffered = this;
promise.done(function () {
dbcheckDeffered.resolve();
console.log('Check finished');
oController._pushTextIntoConsoleModel("output", {"text":"Check finished."});
});
});
The script it calls, has it's own promise as it calls another function:
_checkScript: function(scriptTimerdeferred, bStart, bPreScript){
var oProperties = this.getView().getModel("configModel");
var oParams = oProperties.getProperty("/oConfig/oParams");
var deferred = $.Deferred();
var promise = deferred.promise();
var sCompareStatus1 = "inProg";
var sCompareStatus2 = this._returnHanaCompareStatus(bStart, bPreScript);
var sCompareStatus3 = this._returnHanaCompareStatus3(bStart, bPreScript);
var params = {//some params};
// Send the command
this._sendAWSCommand(params, deferred);
// When command is sent
promise.done(function (oController) {
console.log('back to db check script');
var oCommandOutputModel = oController.getView().getModel("commandOutput");
var sStatus = oCommandOutputModel.Status;
// check that it's not in the wrong status for a start/stop
// or if it's a pre script check -> pre script checks always resolve first time
if(sStatus !== sCompareStatus1 && sStatus !== sCompareStatus2 && sStatus !==sCompareStatus3|| bPreScript){
scriptTimerdeferred.resolve();
}
});
},
This works, however what it does is:
set a timer to call the first script every x seconds (as the data is currently changing - a server is coming online)
the script runs and calls another function to get some data from the DB
when the call for data is resolved (complete) it comes back to 'promise.done' on the checkScript and only resolves the timer promise if it meets certain criteria
all the while, the initial timer is resending the call as eventually the DB will come online and the status will change
I am wondering if there is a better way to do this as currently I could have, for example, 3 calls to the DB that go unresolved then all resolve at the same time. I would prefer to run a command, wait for it to resolve, check the output, if it is not right then run command again.
Thanks!
I think what you want to do can be achieved carefully reading what explained in these links:
Promise Retry Design Patterns
In javascript, a function which returns promise and retries the inner async process best practice
See this jsfiddle
var max = 5;
var p = Promise.reject();
for(var i=0; i<max; i++) {
p = p.catch(attempt).then(test);
}
p = p.then(processResult).catch(errorHandler);
function attempt() {
var rand = Math.random();
if(rand < 0.8) {
throw rand;
} else {
return rand;
}
}
function test(val) {
if(val < 0.9) {
throw val;
} else {
return val;
}
}
function processResult(res) {
console.log(res);
}
function errorHandler(err) {
console.error(err);
}
It retries a promise infinite times since the condition is not satisfied. Your condition is the point you said "check the output". If your check fails, retry the promise. # Be careful to hold a limit case, promises waste memory. If your api/service/server/callreceiver is off, and you don't set a threshold, you could create an infinite chain of promises NO STOP

Resolving promises inside for loop

I am trying to read a JSON object using a for loop to format the JSON data and send it back to the client by putting the formatted response into a model object.
Inside for loop, i am dealing with two promises based upon few conditions. There are two functions, each having a promise returned.How can I get my final data after all the promises are resolved? Thanks in advance.
for (var i = 0, i<jsonData.length; i++){
if(someCOndition){
getSomeData().then(function(data){
//some operation using data
})
}
if(someOtherCOndition){
getSomeOtherData().then(function(data){
//some operation using data
})
}
}
Promise.all([ promise1, promise2 ]) (Promise.all() on MDN) in case of standard JS Promises (ES2015+). It returns a new promise, which gets resolved once all passed promises get resolved. But be aware - it will get rejected immediately when at least one promise gets rejected (it won't wait for any other promise).
You might do as follows;
var promises = [],
JSONData_1 = ["chunk_11","chunk_12","chunk_13"],
JSONData_2 = ["chunk_21","chunk_22","chunk_23"],
getJSONData = (b,i) => new Promise((resolve,reject) => setTimeout(_ => b ? resolve(JSONData_1[i])
: resolve(JSONData_2[i]),1000));
for (var i = 0; i < JSONData_1.length; i++){
if(Math.random() < 0.5) promises.push(getJSONData(true,i));
else promises.push(getJSONData(false,i));
}
Promise.all(promises)
.then(a => console.log(a));
You can use jQuery.when().
var deferredList = [];
for (var i = 0, i<jsonData.length; i++){
if(someCOndition){
deferredList.push(getSomeData().then(function(data){
//some operation using data
}))
}
if(someOtherCOndition){
taskList.push(getSomeOtherData().then(function(data){
//some operation using data
}))
}
}
JQuery.when(taskList).done(function(){
// final to do..
}).fail(){
// even if single one fails ! be aware of this
}
jQuery.when() MDN
You can do it in multiple ways. We can also use for of loop with async..await to get the result synchronously while looping, if that is a requirement. Something like this:
function downloadPage(url) {
return Promise.resolve('some value');
}
async function () {
for(let url of urls) {
let result = await downloadPage(url);
// Process the result
console.log(result);
}
}
You could do something like this..
var arr=[],arr2=[];
for (var i = 0, i<jsonData.length; i++){
if(someCOndition){
//push onto the array inputs for getSomeData()
arr.push(jsonData[i]);
}
if(someOtherCOndition){
arr2.push(jsonData[i]);
}
}
processArr(0);
processArr2(0);
function processArr(idx){
if (idx>=arr.length) {
//done
}
else {
getSomeData().then(function(data){
// some operation using data
// optionally store in a results array
// recurse
processArr(idx+1)
})
}
}
function processArr2(idx){
if (idx>=arr2.length) {
//done
}
else {
getSomeotherData().then(function(data){
// some operation using data
// recurse
processArr2(idx+1)
})
}
}

Best way to write loops with promises (ctx.sync) in JavaScript API for Office

There are many threads that discuss about guaranteeing execution order of promises in loops. I would like to know what is the best practice in JavaScript API for Office Add-ins. Most of the time, the promise in question is ctx.sync().
Here is a snippet to print the address of a list of Excel ranges one by one. The test shows that it respects well the order of Excel ranges. But the question is whether and how to guarantee the execution order?
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
for (var i = 0; i < ranges.length; i++) {
loadRange(ranges[i], sheet);
}
}
function loadRange (range, sheet) {
Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
Could anyone help?
Because Excel.run returns a Promise, you can chain it with a .then and guarantee order. I.e.,
Excel.run(function(ctx) { ... return ctx.sync(); ... })
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
})
.then(function() {
return Excel.run(function(ctx) { ... return ctx.sync(); ... })
});
That being said... this would be pretty dang inefficient. A much better approach would be to load all the objects you need in one batch, creating only one network roundtrip (especially important with Excel Online... but noticeable even on the Desktop):
function loadAll () {
Excel.run(function(ctx) {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
var loadedRanges = [];
for (var i = 0; i < ranges.length; i++) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(ranges[i]);
r.load('address');
loadedRange.push(r);
}
return ctx.sync()
.then(function() {
for (var i = 0; i < loadedRanges.length; i++) {
console.log(loadedRanges[i].address);
}
});
});
}
UPDATE
If, as per comment, you do end up needing to do separate tasks that depend on each other and that each require a roundtrip, and hence do need to be sequenced via chaining Excel.run, I would recommend something as follows:
function loadAll () {
var ranges = ["A:A", "B:B", "C:C", "D:D", "E:E"];
var sheet = "Sheet1";
// Create a starter promise object
var promise = new OfficeExtension.Promise(function(resolve, reject) { resolve (null); });
for (var i = 0; i < ranges.length; i++) {
// Create a closure over i, since it's used inside a function that won't be immediately executed.
(function(i) {
// Chain the promise by appending to it:
promise = promise.then(function() {
return loadRange(ranges[i], sheet);
})
})(i);
}
}
function loadRange (range, sheet) {
return Excel.run(function (ctx) {
var r = ctx.workbook.worksheets.getItem(sheet).getRange(range);
r.load('address');
return ctx.sync().then(function() {
console.log(r.address);
});
});
}
~ Michael Zlatkovsky, developer on Office Extensibility team, MSFT
The update of #Michael Zlatkovsky solves the problem of appending promises perfectly.
The complement added by #SoftTimur allows to wait for all promises to be done before doing a .then(), which is also very convenient !
My only remark about these posts would be if ANY promises throws an error, the other appended promises stop being treated.
In my case, the scenario is a little different. Just to clarify:
Excel.run(function(context){
return runWorkbook(context, context.workbook)
.then(function(){ var cool = "all promises worked !" }
.catch(function(error)) { var bad = "do not want to be here :(" });
}
function runWorkbook(context, workbook){
const sheets = workbook.worksheets;
sheets.load("$none");
return context.sync().then(function(){
let promise = new window.OfficeExtension.Promise(function(resolve, reject) { resolve(null); });
sheets.items.forEach(function(ws) {
promise = promise.then(function() {
return makeWorkOnWorksheet(ws)
.then(context.sync())
.catch(function(error)){
// DO NOTHING BUT CAN NOT THROW ERROR OTHERWISE IT BREAKS THE NEXT APPENDED PROMISES
});
}
return promise;
}
}
This solution works.. (catch the error as in comment and doing nothing with it)
I don't like this solution but this is the only way I found to allow all appended promises to be done.
If somebody has a better idea, it's welcomed ;)
Cheers,

Categories

Resources