Let's say I have a file containing certain promises, that when executed in order, prepares an input file input.txt.
// prepareInput.js
var step1 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
var step2 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
var step3 = function() {
var promise = new Promise(function(resolve, reject) {
...
});
return promise;
};
step1().then(step2).then(step3);
exports.fileName = "input.txt";
If I run node prepareInput.js, the line step1().then(step2).then(step3) gets executed and creates the file.
How can I alter this so that when other files attempt to retrieve fileName from this module, step1().then(step2).then(step3); is run and completed before fileName is exposed? Something along the line of:
// prepareInput.js
...
exports.fileName =
step1().then(step2).then(step3).then(function() {
return "input.txt";
});
// main.js
var prepareInput = require("./prepareInput");
var inputFileName = require(prepareInput.fileName);
Node.js beginner here; apologize beforehand if my approach makes completely no sense... :)
You can't directly export the results of something retrieved asynchronously because export is synchronous, so it happens before any async results have been retrieved. The usual solution here is to export a method that returns a promise. The caller can then call that method and use that promise to get the desired async result.
module.exports = function() {
return step1().then(step2).then(step3).then(function() {
// based on results of above three operations,
// return the filename here
return ...;
});
}
The caller then does this:
require('yourmodule')().then(function(filename) {
// use filename here
});
One thing to keep is mind is that if any operation in a sequence of things is asynchronous, then the whole operation becomes asynchronous and no caller can then fetch the result synchronously. Some people refer to asynchronous as "contagious" in this way. So, if any part of your operation is asynchronous, then you must create an asynchronous interface for the eventual result.
You can also cache the promise so it is only ever run once per app:
module.exports = step1().then(step2).then(step3).then(function() {
// based on results of above three operations,
// return the filename here
return ...;
});
The caller then does this:
require('yourmodule').then(function(filename) {
// use filename here
});
Related
How to implement send function so that calculate will be executed sequentially, with order of calls preserved?
async function calculate(value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value * value), 1)
})
}
async function send(value) {
return await calculate(value)
}
Next call of calculate should not start until the previous call is finished.
Calls to calculate should arrive in the exact same order.
Results for await should be returned correctly.
It should work this way when the caller ignores result (we always return the result and don't care if it's used or not)
send(2)
send(3)
and for async calls too
;(async () => {
console.log(await send(2))
console.log(await send(3))
})()
P.S.
Why it's getting down-voted? It's a perfectly legitimate use case for a stateful remote service, where calculate will be the remote call. And you have to preserve the order because the remote service is stateful and results depends on the call order.
Here's how I set up async queues so that it processes things in order regardless of how they're called:
function calculate(value) {
var reject;
var resolve;
var promise = new Promise((r, rr) => {
resolve = r;
reject = rr;
})
queue.add({
value: value,
resolve: resolve,
reject: reject
});
return promise;
}
var calcluateQueue = {
list: [], // each member of list should have a value, resolve and reject property
add: function(obj) {
this.list.push(obj); // obj should have a value, resolve and reject properties
this.processNext();
},
processNext: async function() {
if (this.processing) return; // stops you from processing two objects at once
this.processing = true;
var next = this.list.unshift(); // next is the first element on the list array
if (!next) return;
try {
var result = await doSomeProcessing(next.value);
next.resolve(result);
this.processNext();
} catch(e) {
next.reject(e);
// you can do error processing here, including conditionally putting next back onto the processing queue if you want to
// or waiting for a while until you try again
this.processNext();
}
}
};
I would like to optimize multiple expensive server calls by repeatedly calling a function that takes a key, and returns a promise of an object. When resolved, the object is guaranteed to contain the needed key + some value, and it could contain other unrelated keys. The function would:
on first call, create a promise
on each call, accumulate keys to be sent to the server
all calls return the same promise until 100ms of quiet time
when no calls are made for 100 ms, call the server to process all the keys accumulated so far
if a new call is made, even if the server hasn't responded yet, treat it as the "first call" by starting a new promise with its own set of pending keys
when server call returns, resolve the pending promise
Are there any NPM libs that would help with this, or should I write it from scratch?
Searching for "NPM consolidate server requests using a single promise" or "... accumulate server requests... " didn't turn up anything obvious. I'll share the mockup code using ES6 promises mentioned in comment to perhaps form the basis of a solution in the absence of other suggestions. As is, not guaranteed etc...
/******* application code ********/
function requestKeys( keyArray) {
// promise an oject for values of keys in keyArray:
// use asynchronous code to get values for keys in keyArray,
// return a promise for the parsed result object.
// ...
}
const latency = 100; // maximum latency between getting a key and making a request
/******** generic code *********/
var getKey = (( requestKeys, latency) => {
// return a function to return a promise of a key value object
var promise = null;
var resolve = null;
var reject = null;
var pendingKeys = null;
var defer = () => {
promise = new Promise( (r,j) => {resolve = r; reject = j});
pendingKeys = [];
};
var running = false;
var timesUp = () => {
resolve( requestKeys( pendingKeys));
running = false;
}
var addKey = ( key) => {
if(! running) {
defer();
setTimeout( timesUp, latency);
running = true;
}
pendingKeys.push( key);
return promise;
}
return addKey;
})( requestKeys, latency);
/******* test code *******/
// redefine requestKeys to promise an object with key strings as key values,
// resolve the return promise synchronously for testing:
function requestKeys( keyArray) {
var keyObj = keyArray.reduce( ((obj, v) => ((obj[v] = v), obj) ), {} );
return new Promise( (resolve, reject) => resolve(keyObj) );
}
var log = obj => console.log( JSON.stringify(obj));
// get two keys quickly
getKey("holas").then( log);
getKey("buono").then( log);
// wait and get another
setTimeout( function(){getKey('later').then( log)}, 500);
I have 3 Node Js functions. What I'm trying to do here is, I want to call normalizeFilePath and get the normalized path, after that check whether a file exist or not with that normalizedFilePath and after all these, create a file if the file doesn't already exist. This is the first day of using promises (Bluebird) and I'm new to Node JS and Java Script. Below code structure is getting complex. Of course this is not a good idea at all.
var createProjectFolder = function (projectName) {
};
var checkFileExistance = function (filePath) {
return new promise(function (resolve, reject) {
normalizeFilePath(filePath).then(function (normalizedFilePath) {
return fs.existSync(normalizedFilePath);
});
})
};
var normalizeFilePath = function (filePath) {
return new promise(function (resolve, reject) {
resolve(path.normalize(filePath));
});
};
How can i manage promises to implement that concept?
Let's improve your code in two simple steps.
Promises are meant for async functions
As long as path.normalize is synchronous, it should not be wrapped in promise.
So it can be as simple as that.
var normalizeFilePath = function (filePath) {
return path.normalize(filePath);
};
But for now lets pretend that path.normalize is async, so we can use your version.
var normalizeFilePath = function (filePath) {
return new Promise(function (resolve, reject) {
resolve( path.normalize(filePath) );
});
};
Promisify all the things
Sync is bad. Sync blocks event loop. So, instead of fs.existsSync we will use fs.exists.
var checkFileExistance = function (filePath) {
return new Promise(function (resolve, reject) {
fs.exists(filePath, function (exists) {
resolve(exists);
});
});
};
As You can see, we are wrapping async function that accepts a callback with a promise. It's quite a common concept to "promisify" a function, so we could use a library for that. Or even use fs-promise, that is -- you guess it -- fs with promises.
Chaining promises
Now, what we want is making three actions one after another:
Normalize file path
Check if file already exists
If not, create a directory
Keeping that in mind, our main function can look like this.
var createProjectFolder = function (projectName) {
normalizeFilePath(projectName)
.then(checkFileExistance)
.then(function (exists) {
if (!exists) {
// create folder
}
})
.catch(function (error) {
// if there are any errors in promise chain
// we can catch them in one place, yay!
});
};
Don't forget to add the catch call so you would not miss any errors.
I have a promise call that is supposed to do the following:
1) Get a link from completedLinks
2) Use cheerio to load the html into the $, like jQuery.
3) get the title, and other details from the page
4) save those details to the object siteObj
JS:
completedLinks = ["http://www.example.com/1","http://www.example.com/2","http://www.example.com/3"...]
function fetchSiteDetails(arr) {
return arr.reduce(function(promise, link) {
console.log(link);
var siteObj = {};
var $ = cheerio.load(link);
var titles = $("title").text().trim();
var html = $("#content").html().trim();
var mainContent = $(".left-side").html().trim();
var allLinks = $('a');
siteObj.title = titles;
siteObj.html = html;
siteObj.mainContent = mainContent;
siteObj.subPages = [];
allLinks.each(function(i, elem) {
siteObj.subPages.push($(this).attr('href'));
});
return promise;
}, Promise.resolve());
}
fetchSiteDetails(completedLinks).then(function() {
console.log('all done');
});
Right now it is not completing each task before it starts the next, and finishes before it's done anything.
As I can't see what is asynchronous in your code, here's a generic pattern to run asynchronous operations in series using promises and array.reduce
return arr.reduce(function(promise, link) {
return promise.then(function(resultOfPreviousPromise) {
// your code goes here
// return a promise that resolves when the asynchronous function completes
});
}, Promise.resolve());
What will interest you is the bit where that says "your code goes here" - which is where you should put your code. The return value needs to be a promise that is resolved when the asynchronous operation completes
I'm new to Javascript and AngularJS and this one makes me scratch my head :/
Precondition
A REST Service providing my data from the backend
AngularJS 1.2.21 and Restangular 1.4.0
An AngularJS controller, that shall ask the service for a spiced up version of the provided
What I have
This is the method in question:
service.getSlices = function() {
Restangular.all('entries').getList().then(function(entries) {
//some rather complex modification of the backend data go here
//...
return resultOfModification; //this is what should be returned for getSlices();
})
//I want the resultOfModification to be returned here
};
The question
Bascially I would like to wait in getSlices() until the promise is resolved in order to return my resultOfModification only when it actually is calculated.
Additional scenario
I could also image to return a promise from getSlices() which would then provide the resultOfModification. However I fear I do not understand this well enough and / or am too frustrated / tired meanwhile.
Answers and any suggestions are welcome, especially pointers to good reading material. Thanks
You can't return it at that place as actual value, because Restangular is async (the function getSlices is left before the callback you pass to then is called). That's why Promise is used.
Even if it would be possible to make Restangular to be sync you shouldn't do that because this will block the browser until the data is requested which will be a bad user experience.
You should try to get into Promise as they where designed to look like sync code but behave async.
The thing you would need to change in your code is to add a return before the Restangular.all :
service.getSlices = function() {
return Restangular.all('entries').getList().then(function(entries) {
//some rather complex modification of the backend data go here
//...
return resultOfModification; //this is what should be returned for getSlices();
})
};
This will return the Promise that is return by the .then call. This Promise will resolve to resultOfModification as this is the vale you return form its callback.
That way you could use getSlices that way:
service.getSlices().then(function(modifiedData) {
});
Promises can be chained up:
(new Promise(function( resolve, reject){
setTimeout(function() {
resolve("some");
},200);
}))
.then(function(data) {
return data+' data';
})
.then(function(data) {
//here a Promise is return which will resovle later (cause of the timeout)
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(data+' !!!!!!');
},200);
});
})
.then(function(data) {
//this will have 'some data !!!!!!'
console.log(data);
});
Which would be the same as if you would write it that way:
var promiseA = new Promise(function( resolve, reject){
setTimeout(function() {
resolve("some");
},200);
});
var promiseB = promiseA.then(function(data) {
return data+' data';
})
var promiseC = promiseB.then(function(data) {
//here a Promise is return which will resovle later (cause of the timeout)
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve(data+' !!!!!!');
},200);
});
});
var promiseD = promiseC.then(function(data) {
//this will have 'some data !!!!!!'
console.log(data);
});