I am using Dojo 1.10 by the way which came with Deferred and Promise API. So the scenario is as below:
var deferred_1 = new Deferred();
deferred_1 .then(function(value){
// do something
return something;
})
var deferred_2 = new Deferred();
deferred_2.then(function(value){
// do something
return something;
})
var completeFunc = function(value){
console.log("done");
}
//run completeFunc after completion of both deferred_1.then and
deferred_2.then
How do I ensure the completion of both deferred objects callback functions in deferred_1.then and deferred_2.then are completed before running another function.
Use Promise.all to create a Promise that resolves only after all promises you pass into it are resolved. Note that you have to save a reference to the new promises:
const deferred_1 = new Deferred();
const d1 = deferred_1.then(function(value) {
// do something
return something;
});
const deferred_2 = new Deferred();
const d2 = deferred_2.then(function(value) {
// do something
return something;
})
const completeFunc = function(value) {
console.log("done");
}
Promise.all([d1, d2]).then(completeFunc);
You can use dojo/promise/all function.
Example from documentation:
require(["dojo/promise/all"], function(all){
all([promise1, promise2]).then(function(results){
// results will be an Array
});
// -- or --
all({
promise1: promise1,
promise2: promise2
}).then(function(results){
// results will be an Object using the keys "promise1" and "promise2"
});
});
Related
I am trying to write a series of AJAX requests into a dictionary.
I am attempting to use promises for this, however I am either writing the promise syntax incorrectly, or what I think may be happening is that the function is actually completing (for loop is done, and AJAX requests sent) but the AJAX requests are still not returned. Therefore this is still returning an empty dictionary.
let dict = {};
let activeMachines = ["41", "42", "43"];
let dataPromise = new Promise (function (resolve, reject)
{
for (let i = 0; i < activeMachines.length; i++)
{
let machineID = activeMachines[i]
let getAPIData = new XMLHttpRequest();
let url = 'http://127.0.0.1:8000/processes/apidata/' +machineID + '/';
getAPIData.open('GET', url);
getAPIData.send();
getAPIData.onload = function()
{
let APIData = JSON.parse(getAPIData.responseText);
dict['machine_' + machineID] = APIData[0].author_id;
dict['temp' + machineID] = APIData[0].tempData; //get value
dict['humid' + machineID] = APIData[0].humidData;
timeValue = String((APIData[0].dateTime));
dict['time' + machineID] = new Date(timeValue);
console.log("done");
}
}
resolve();
});
dataPromise.then(function() {console.log(dict);});
Is there a way to "sense" when all of the XMLHTTPRequests have returned?
#Rafael's answer will work, but it doesn't illuminate much about what's going wrong, since you're trying to grok the concept of Promises and write one yourself.
Fundamentally I think your approach has two missteps: 1. creating a single Promise that handles calls to all of your arbitrary list of "activeMachines", and 2. putting your resolve() call in the wrong place.
Usually a Promise looks like this:
const myPromise = new Promise(function(resolve, reject) {
doSomeAsyncWork(function(result) {
// Some kind of async call with a callback function or somesuch...
resolve(result);
});
}).then(data => {
// Do something with the final result
console.log(data);
});
You can simulate some kind of arbitrary asynchronous work with setTimeout():
const myPromise = new Promise(function(resolve, reject) {
// Resolve with "Done!" after 5 seconds
setTimeout(() => {
resolve("Done!");
}, 5000);
}).then(data => {
console.log(data); // "Done!"
});
However your original code puts the resolve() call in a weird place, and doesn't even pass it any data. It looks sorta equivalent to this:
const myPromise = new Promise(function(resolve, reject) {
// Resolve with "Done!" after 5 seconds
setTimeout(() => {
// Doing some work here instead of resolving...
}, 5000);
resolve();
}).then(data => {
console.log(data); // This would be "undefined"
});
Where you're doing a console.log("done"); in your original code is actually where you should be doing a resolve(someData);!
You're also trying to do side effect work inside of your Promise's async function stuff, which is really weird and contrary to how a Promise is supposed to work. The promise is supposed to go off and do its async work, and then resolve with the resulting data -- literally with the .then() chain.
Also, instead of doing multiple asynchronous calls inside of your Promise, you should generalize it so it is reusable and encapsulates only a single network request. That way you can fire off multiple asynchronous Promises, wait for them all to resolve, and then do something.
const activeMachines = ["41", "42", "43"];
// Make a reusable function that returns a single Promise
function fetchAPI(num) {
return new Promise(function(resolve, reject) {
const getAPIData = new XMLHttpRequest();
const url = "http://127.0.0.1:8000/processes/apidata/" + num + "/";
getAPIData.open("GET", url);
getAPIData.send();
getAPIData.onload = function() {
const APIData = JSON.parse(getAPIData.responseText);
const resolveData = {};
resolveData["machine_" + num] = APIData[0].author_id;
resolveData["temp" + num] = APIData[0].tempData; //get value
resolveData["humid" + num] = APIData[0].humidData;
timeValue = String(APIData[0].dateTime);
resolveData["time" + num] = new Date(timeValue);
resolve(resolveData);
};
});
}
// Promise.all() will resolve once all Promises in its array have also resolved
Promise.all(
activeMachines.map(ea => {
return fetchAPI(ea);
})
).then(data => {
// All of your network Promises have completed!
// The value of "data" here will be an array of all your network results
});
The fetch() API is great and you should learn to use that also -- but only once you understand the theory and practice behind how Promises actually operate. :)
Here's an example of the Fetch API which uses Promises by default:
let m_ids = [1,2,3,4];
let forks = m_ids.map(m => fetch(`http://127.0.0.1:8000/processes/apidata/${m}`));
let joined = Promise.all(forks);
joined
.then(files => console.log('all done', files))
.catch(error => console.error(error));
I hope this helps!
I want to create a function that enqueues callback functions, but doesn't return until that callback has been dequeued and executed.
function executeCallback(callback) {
if( shouldEnqueue ) {
enqueue(callback);
// awaits execution of callback
// return executed callback's return value
} else {
return callback()
}
}
The actual function works as follows. When an orderbook is needed, we will check to see if we have a recent one cached. If we don't have a recent one, we will queue the request for a fresh orderbook. We will then return the orderbook once it has been requested. The actual code will look something like this:
const axios = require("axios");
const moment = require("moment");
const { promisify } = require("util");
const orderbookQueue = [];
const orderbookUpdateTime = {};
async function requestOrderbook(pair) {
const response = await axios.post("https://api.kraken.com/0/public/Depth", {
pair
});
orderbookUpdateTime[pair] = +moment();
return response.data.result[pair];
}
async function getOrderbook(pair, currentTime) {
if (
orderbookUpdateTime[pair] &&
currentTime - orderbookUpdateTime[pair] > 60000
) {
const requestOrderbookAsync = promisify(requestOrderbook);
orderbookQueue.push(() => requestOrderbookAsync(pair));
// how to I return the result of requestOrderbookAsync when it has been called ?
}
return requestOrderbook(pair);
}
const queueExecutor = function queueExecutor() {
while (orderbookQueue.length) {
orderbookQueue[0]();
orderbookQueue.shift();
}
};
1. Convert you callbacks to promise
E.g, convert
function mutilpy(a, b, callback) {
let result = a*b;
return callback(result);
}
to
function multiply(a, b) {
return new Promise(function(resolve, reject) {
let result = a*b;
resolve(result);
})
}
2. (Sort of) Enqueue your callbacks for completion
var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = multiply(10,3);
Promise.all([promise1, promise2, promise3]).then(function(values) {
// YOU GET HERE WHEN ALL PROMISES ARE RESOLVED
console.log(values);
});
// expected output: Array [3, 42, 30]
I found a solution that works for me. One can extract the resolve function from a new Promise, enqueue the resolve function in a different scope, then dequeue and execute the resolve function whenever the time comes, in any scope.
Resolve Javascript Promise outside function scope
I fully understand that similar questions have been asked before but I don't get it running. I need a chain of promises and I need to wait until all promises are resolved.
My issue is that I need to call different promises based on the outcome of another promise :-(
So based on the outcome of promise2 my promise4 is EITHER $translate.use OR it is $translate.refresh.
This is what I have so far (simplified):
var promise1 = someService.get({}).$promise.then(function (result) {
// do stuff
});
var promise2 = anotherService.getPromise().then(function (result) {
var properties = result[0];
// do stuff
return properties;
});
var promise3 = promise2.then(function(properties){
// using $translate (angular-translate) here which is async as well
var userLanguage = properties.language;
if (userLanguage !== $translate.preferredLanguage()) {
// either this is the 4th promise
$translate.use(userLanguage).then(function (myVar) {
// ...
});
} else {
// or this is the 4th promise
$translate.refresh().then(function (myVar) {
// ...
});
}
});
var loadPromises = {
promise1: promise1
promise2: promise2
promise3: promise3
promise4: ???
};
$q.all(loadPromises).then(function (result) {
// anything done
});
You don't really need to monitor promise3 and promise4, all you need is promise1 and promise2. promise3 becomes next step of the promise2. Just note how you return new promise (either return $translate.use or return $translate.refresh) from promise2 then part :
var promise1 = someService.get({}).$promise.then(function(result) {
// do stuff
});
var promise2 = anotherService.getPromise().then(function(result) {
var properties = result[0];
// do stuff
return properties;
})
.then(function(properties) {
// using $translate (angular-translate) here which is async as well
var userLanguage = properties.language;
if (userLanguage !== $translate.preferredLanguage()) {
// either this is the 4th promise
return $translate.use(userLanguage).then(function(myVar) {
// ...
});
} else {
// or this is the 4th promise
return $translate.refresh().then(function(myVar) {
// ...
});
}
});
var loadPromises = {
promise1: promise1,
promise2: promise2
};
$q.all(loadPromises).then(function(result) {
// anything done
});
If I understand your code well enough, I think you only need to return the inner promise inside of promise3. In your example, both promise1 and promise2 won't be resolved until the service is done. Since promise3 depends on promise2 it won't finish until promise2 resolved. I believe $q.all keeps going until all promises are complete, even promises returned by promises. Since promise3 returns a promise, promise3 won't be considered resolved until the inner promise is resolved. So, I think adding a couple return statements is all that you need:
var promise1 = someService.get({}).$promise.then(function (result) {
// do stuff
});
var promise2 = anotherService.getPromise().then(function (result) {
var properties = result[0];
// do stuff
return properties;
});
var promise3 = promise2.then(function(properties){
// using $translate (angular-translate) here which is async as well
var userLanguage = properties.language;
if (userLanguage !== $translate.preferredLanguage()) {
// either this is the 4th promise
//NOTE: added return here
return $translate.use(userLanguage).then(function (myVar) {
// ...
});
} else {
// or this is the 4th promise
//NOTE: added return here
return $translate.refresh().then(function (myVar) {
// ...
});
}
});
var loadPromises = {
promise1: promise1,
promise2: promise2,
promise3: promise3
};
$q.all(loadPromises).then(function (result) {
// anything done
});
In the below code, I want sequential executuon of the method saveBulkUploadSinglePacket in the while loop, that means process next packet after completion of the current packet. How to achieve that.
var saveBulkUploadSinglePacket = function(){
while (packetCount<=bulkUploadPackets.length){
$.when(saveBulkUploadSinglePacket(modelToSave)).done(function(arguments){
saveBulkUploadPackets.push(arguments);
packetCount++;
});
}
return saveBulkUploadPackets;
}
var saveBulkUploadSinglePacket = function(modelToSave){
var defer = $.Deferred();
$.when(new SaveBulkUpload().save(modelToSave)).done(function(arguments){
defer.resolve(arguments);
}).fail(function(errorObj){
defer.reject(errorObj);
});
return defer.promise();
}
The standard way to say "perform x when promise is done" is through promise.then(). Keep track of the current promise in a var outside the loop and attach each call to the previous promise with a .then():
var saveBulkUploadSinglePacket = function(){
var lastPromise = $.when();
while (packetCount<=bulkUploadPackets.length){
lastPromise = (
lastPromise
.then(function(){
// make sure saveBulkUploadSinglePacket returns a promise
return saveBulkUploadSinglePacket(modelToSave));
})
.then(function(){
saveBulkUploadPackets.push(arguments);
packetCount++;
// return a resolved promise
return $.when();
})
);
}
lastPromise.resolve(saveBulkUploadPackets);
return lastPromise;
}
At the end of the function I resolved the final promise with the desired return value, then returned the promise. This way you can call saveBulUploadSinglePacket().then(...) to wait for all the promises to complete and handle the result.
To save your pakets sequentially, use Array.prototype.reduce() to build a .then() chain very concisely, as follows :
var saveBulkUploadPackets = function(packets) {
return packets.reduce(function(promise, paket) {
return promise.then(function(results) {
return (new SaveBulkUpload()).save(paket).then(function(res) {
return results.concat(res);
});
});
}, $.when([]));
}
And call like this :
saveBulkUploadPackets(bulkUploadPackets).then(function(results) {
// All done.
// use/log the `results` array here.
});
I'm using Parse.com. There are many drills inside each package and many packages in each category.
I'm stuck on .then(function(result, result2, result3) on the last line. The promise can have multiple promises and it is variable. Is there a way to get write something like:
.then(function(multipleResults);
console.log(mulitpleResults);
Thanks!
var Parse = require('parse/node').Parse;
var _ = require('underscore');
var TrainingPackage = Parse.Object.extend("TrainingPackage");
var TrainingCategory = Parse.Object.extend("TrainingCategory");
var query2 = new Parse.Query(TrainingPackage);
var query = new Parse.Query(TrainingCategory);
query.equalTo("objectId", "kfHnYdd3T1");
query.find().then(function(cat){
query2.equalTo("category_id", cat[0]);
return query2.find();
}).then(function(results){
var promises = [];
_.each(results, function(result) {
var Drill = Parse.Object.extend("Drill");
var query3 = new Parse.Query(Drill);
query3.equalTo("package_id", result);
promises.push(query3.find());
});
return Parse.Promise.when(promises);
}).then(function(result, result2, result3){
console.log(result);
console.log(result2);
console.log(result3);
});
Due to the inherent nature of a promise it may only resolve to a single value. This being said what you probably want to do is take a look at the Promise.all API. Promise.all will create a new Promise from an array of promises and it will resolve when all your promises are resolved. E.g.
Promise.all([promise1, promise2]).then(function(results) {
// Both promise1 and promise 2 resolved and available at variable results
})
Working example from Mozilla Promise API:
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "foo");
});
Promise.all([p1, p2, p3]).then(function(values) {
console.log(values); // [3, 1337, "foo"]
});