Create a Javascript promise in a dormant state? - javascript

One of the issues I have faced when crafting Promises for certain app context is that of wanting to delay any execution of the code inside the promise until later. This happens frequently when I have manager objects that maintain a collection of Promises for execution at a later time. To remedy this, I end up creating builder functions that are called by the manager objects at the time the Promise needs to be executed. This is tedious and leads to a fair amount of "boilerlate" code.
For example, here's one of my Promise builder functions:
this._buildPollingPromise = function(ethTransWaiter) {
return new Promise(function(resolve, reject) {
// Execute the function that builds a polling method promise.
ethTransWaiter.confirmTransPromiseBuilder.buildPromise()
.then(function(result) {
...
})
.then(function(ignoreResult) {
resolve(ethTransWaiter.isConfirmed);
})
.catch(function(err)
{
// Reject the promise with the error received.
reject(err);
});
});
}
I have to delay execution of the ethTransWaiter.confirmTransPromiseBuilder.buildPromise() method because if it executes at the time the Promise is created, it will fail because the conditions aren't in place yet for it to execute successfully.
Therefore, I am wondering if there is a built-in method, or an NPM package, that creates or helps create Promises that can be built in a dormant state, so that the code in the function that lives inside the Promise constructor does not execute until some later time (i.e. - at the precise time when you want it to execute) That would save me a lot of boilerplate coding.

May be something like this?
function wait(ms) {
return new Promise(function (resolve) {
setTimeout(resolve, ms);
});
}
this._buildPollingPromise = function (ethTransWaiter) {
var waitUntilLater = wait(3000);
var buildPromise = new Promise(function (resolve, reject) {
// Execute the function that builds a polling method promise.
ethTransWaiter.confirmTransPromiseBuilder.buildPromise()
.then(function (result) {
//...
})
.then(function (ignoreResult) {
resolve(ethTransWaiter.isConfirmed);
})
.catch(function (err) {
// Reject the promise with the error received.
reject(err);
});
});
return Promise.all([waitUntilLater, buildPromise]).then(function (results) {
return results;
});
}

Related

Why we are not chaining promises inside each other?

in one of youtube tutorial videos about promises I found following code:
let cleanRoom = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let removeGarbage = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
let winIcecream = function() {
return new Promise((resolve, reject) => {
resolve();
});
};
cleanRoom().then(function(){
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
Why the promises aren't chained like that the every .then word is after the previous promise? I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then(), how is it happening that the winIcecream() will run after resolving the removeGarbage promise?
Also do I need to type return declaring every promise like in the code above? If yes, why do I need to do so?
Your initial questions may be answered by rewriting things to variable assignments.
I'm using arrow function syntax to implicitly return the new expression here; if you're using regular functions, then yes, you do have to return the new chained promise if you want to run them in sequence.
const roomCleanedPromise = cleanRoom();
const roomCleanedAndGarbageTakenOutPromise = roomCleanedPromise.then(() => removeGarbage());
const roomCleanedAndGarbageTakenOutAndIcecreamWonPromise = roomCleanedAndGarbageTakenOutPromise.then(() => winIcecream());
const finishedPromise = roomCleanedAndGarbageTakenOutAndIcecreamWonPromise.then(() => console.log('finished'));
However things are easier written using the more modern async/await syntax - the YouTube tutorial you mention is a little outdated, perhaps.
async function cleanRoom() {
console.log('Cleaning room.');
// this could do other async things or just take a while
return {room: 'clean'}; // just to demonstrate a return value
}
async function removeGarbage() {
console.log('Removing garbage.');
// this could do other async things or just take a while
return {garbage: 'removed'};
}
// third function elided for brevity
async function doAllTheThings() {
const roomStatus = await cleanRoom();
const garbageStatus = await removeGarbage();
console.log('finished');
}
The purpose of using a fulfillment handler (the functions passed to then in your example) is to wait for the promise to be fulfilled and then, at that point, do something else.
The goal of that code (apparently) is to wait until the cleanRoom promise is fulfilled, then start the removeGarbage process, and then when that is fulfilled, start the winIcecream process. It's also worth noting that if cleanRoom's promise was rejected instead of being fulfilled, removeGarbage wouldn't happen at all, because it's in a fulfillment handler, not a rejection handler.
If you did this instead:
cleanRoom().then(function() { /*...*/ });
removeGarbage().then(function() { /*...*/ });
winIcecream().then(function() { /*...*/ });
...all three processes would be started immediately and run in parallel (to the extent whatever async process they're modelling can run in parallel with other JavaScript code). There'd be no coordination between them at all.
...how is it happening that the winIcecream() will run after resolving the removeGarbage promise...
then, catch, and finally create and return new promises. Those promises are fulfilled or rejected based on what happens to the promise they were called on and what happens in or is returned by their handler. So for example:
doThis()
.then(function() { return doThat(); })
.then(function() { console.log("done"); });
Let's call the promise from doThis() "Promise A". Calling then on it creates a new promise ("Promise B"), that will either be rejected (if Promise A is rejected) or will call its handler if Promise A is fulfilled. Promise B is resolved to whatever that handler returns. In the code above, suppose Promise A is fulfilled and doThat() returns a promise ("Promise C"). Now, Promise B is resolved to Promise C — whatever happens to Promise C is what will happen to Promise B. If Promise C is fulfilled, Promise B is fulfilled, and the second handler with the console.log is called.
The MDN article on using promises may be helpful.
Your suggestion / thinking I mean, why for example .then is not immediately after removeGarbage() but it is after the cleanRoom().then() is wrong.
Each promise ( .then ) is not execute immediately.
You can take a look at your example ( little edited by me )
const cleanRoom = () => new Promise((resolve, reject) => {
resolve();
});
const removeGarbage = () => new Promise((resolve, reject) => {
resolve();
});
const winIcecream = () => new Promise((resolve, reject) => {
resolve();
})
cleanRoom().then(function(){
console.log('second');
return removeGarbage();
}).then(function() {
return winIcecream();
}).then(function() {
console.log('finished');
})
console.log('first');
You should read more how the event loop works.

The batch function passed to the ".run" method didn't return a promise

Although I do realize what the error means and why it happens, I think I have a use case that goes outside the expected. I am using Word.run() inside another promise, like so:
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
resolve(someData);
});
});
So, if I understood it correctly, this resolves my promise, but leaves the .run method hanging since there's no return context.sync() at the end? Or did I get it wrong? If I'm right, how can I rewrite the example above to keep .run working properly?
If you want your promise to resolve after the context syncs...
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
return context.sync().then(function() {
resolve(someData); // promise will resolve after sync resolves
});
});
});
If you don't need to resolve after but just want to sync the context at some point in the future, you can actually do this and it won't wait for the sync:
return new Promise((resolve, reject) => {
window.Word.run(context => {
// do stuff with context
resolve(someData); // this will resolve the promise
return context.sync(); // this will actually still happen since the function never returns
});
});

Promise syntax / alternative [duplicate]

This question already has answers here:
Why does the Promise constructor need an executor?
(2 answers)
Closed 6 years ago.
I wish to return a Promise which is self-resolved at a later time, but it seems that my syntax is invalid, and I'm curious what a better implementation would be. Why is an executor required for the Promise constructor?
promise = new Promise() is invalid because I need to supply a function
function getDetails (someHash) {
var url = "http://somewebsite.com/" + someHash,
promise = new Promise();
makeAjaxRequest(url, function (response) {
promise.resolve(response.data);
});
setTimeout(function () {
promise.reject("timeout");
}, 500);
return promise;
}
function makeAjaxRequest (url, callback) {
var someResponse = {data: "some data"};
setTimeout(function () {
callback(someResponse);
}, 200);
}
Is there a better way to implement this functionality?
Note: If you want to convert a callback API to promises see this question.
Let's start with something that should be said from the get go. An executor is not required in order to design promises. It is entirely possible to design a promises implementation that does something like:
let {promise, resolve, reject} = Promise.get();
If you promise not to tell anyone, I'll even let you in on a little secret - this API even exists from a previous iteration of the spec and in fact even still works in Chrome:
let {promise, resolve, reject} = Promise.defer();
However, it is being removed.
So, why do I need to pass an executor?
I just answered your other question about why an executor is an interesting design. It's throw-safe and it lets you take care of interesting things.
Can I still get the resolve, reject functions? I need those
In my experience, most of the times I needed resolve/reject I didn't actually need them. That said - I did in fact actually need them a couple of times.
The specifiers recognized this and for this reason the executor function is always run synchronously. You can get the .defer interface it just isn't the default:
function defer() {
let resolve, reject, promise = new Promise((res, rej) => {
[resolve, reject] = [res, rej];
});
return {resolve, reject, promise};
}
Again, this is typically not something you want to do but it is entirely possible that you have a justifiable use case which is why it's not the default but completely possible.
Your actual code
I would start with implementing things like timeout and a request as primitives and then compose functions and chain promises:
function delay(ms) {
return new Promise(r => setTimeout(r, ms));
}
function timeout(promise, ms) {
return Promise.race([
promise,
delay(ms).then(x => { throw new Error("timeout"); })
]);
}
function ajax(url) { // note browsers do this natively with `fetch` today
return new Promise((resolve, reject) => { // handle errors!
makeAjaxRequest(url, (result) => {
// if(result.isFailure) reject(...);
if(result.status >= 400) reject(new Error(result.status));
else resolve(result.data);
});
});
}
Now when we promisified the lowest level API surface we can write the above code quite declaratively:
function getDetails (someHash) {
var ajax = makeAjaxRequest("http://somewebsite.com/" + someHash);
return timeout(ajax, 500);
}
You need to pass a function to the Promise constructor (more info), which will get called to provide the resolve and reject functions:
function getDetails (someHash) {
var url = "http://somewebsite.com/" + someHash;
return new Promise(function(resolve, reject) {
makeAjaxRequest(url, function(err, response) {
if (err)
reject(err);
else
resolve(response.data);
});
setTimeout(function () {
reject("timeout");
}, 500);
});
}
function makeAjaxRequest (url, callback) {
var someResponse = {data: "some data"};
setTimeout(function () {
callback(null, someResponse);
}, 200);
}
I've also taken the liberty to make makeAjaxRequest use the standard convention of passing errors as first argument (because I assume that at some point you want to replace the setTimeout() with an actual AJAX request).

Promise not returning properly

I have the following function. It calls itself repeatedly and iterates through ftp servers checking for new files. I'm trying to make it a promise so that I can operate().then(function(newFilesObject), but I can't get the .then on operate to activate. It does attempt to resolve it but doesn't send through. By the way, newFiles is a global variable that gets the files per server appended to it. If more code is wanted I can post or github it.
function operate(){
return new Promise(function(resolve, reject){
if(servers[i]){
if(i!== 0) ftp = new JSFtp(servers[i].server)
local = servers[i].local
remote = servers[i].remote
localFiles = fs.readdirSync(local)
}else{
console.log('trying to resolve')
console.log(newFiles)
resolve(newFiles)
}
gatherFiles(remote).then(function(files){
if(files.length>0){
downloadNew(files).then(function(){
console.log('Done: ' + servers[i].server.host)
i++
operate()
})
}else{
console.log('No updates: ' + servers[i].server.host)
i++
operate()
}
})
})
}
operate().then(function(files){
console.log('files: ' + files)
})
The promises in the code sample do not return as their resolvers or rejectors are not always invoked. In fact, resolve is only invoked when i === 0. According to the Promises/A+ specification, promises may only be transitioned to a fulfilled state by invoking resolve. It also can only be transitioned to a rejected state by invoking reject or throwing an exception from within the executor. Therefore, reaching the end of the executor without invoking either or passing one as a callback ensures the promise remains in pending state indefinitely.
The goal you seek may be achieved with a little refactoring. Considering the following as your goal:
Sequentially through each FTP server...
Read a given directory for a list of files
Compare list of files to one stored locally to determine new ones
If there are new ones, download them sequentially
Return a list of all newly downloaded files
Data
var knownFTPServers = [{
'localDirectory': 'sub/',
'localFilepaths': ['docA.json', 'docB.json'],
'remoteDirectory': 'remsub/',
'remoteFilepaths': [],
'jsftpHandle': undefined,
'host': 'example.com'
},
{
'localDirectory': 'root/',
'localFilepaths': ['file1.txt', 'file2.txt'],
'remoteDirectory': 'remroot/',
'remoteFilepaths': [],
'jsftpHandle': undefined,
'host': 'geocities.com'
}];
Logic
function pullNewFilesFromFTPServer(ftpServer) {
return new Promise(function (resolve, reject) {
var handle = new JSFtp(ftpServer);
ftpServer.jsftpHandle = new JSFtp(ftpServer);
// Returns a promise for reading a directory from JSFtp server
// resolves with file list
// rejects with FTP error
function readdir(directory) {
return new Promise(function (resolve, reject) {
handle.ls(ftpServer.remoteDirectory, function (err, res) {
if (err) return reject(err);
resolve(res);
});
});
}
// Returns a promise for downloading a file from a remote JSFtp server
// resolves with the filepath of the downloaded filepath
// rejects with FTP error
function downloadFile(path) {
return new Promise(function (resolve, reject) {
handle.get(path, path, function (err) {
if (err) return reject(err);
resolve(path);
});
});
}
// get all remote filepaths on server
readdir(ftpServer.remoteDirectory)
// filter out filepaths already present locally
.then(function (remoteFilepaths) {
return remoteFilepaths.filter(function (path) {
return ftpServer.localFilepaths.indexOf(path) < 0;
});
})
// download new filepaths sequentially
// reduce turns the array of new filepaths into a promise chain
// return new filepaths after completing the promise chain
.then(function (newFilepaths) {
return newFilepaths.reduce(function (previousDownloadPromise, newPath) {
return previousDownloadPromise.then(function () {
return downloadFile(newPath);
});
}, Promise.resolve())
.then(function () { return newFilepaths; });
})
// resolve server promise with new filepaths or reject with errors
.then(resolve, reject);
});
}
var allFilesDownloaded = [];
knownFTPServers.reduce(function (previousServerPromise, server) {
return previousServerPromise.then(function (filesDownloaded) {
allFilesDownloaded = allFilesDownloaded.concat(filesDownloaded);
return pullNewFilesFromFTPServer(server);
});
}, Promise.resolve([]))
.then(function () {
console.log(allFilesDownloaded);
}, function (err) {
console.err(err);
});
Though it may seem a little more complicated in some places, the actions of each function are more modular. The idea that is somewhat unintuitive is using Array.prototype.reduce to turn an array of data into an array of promises executed sequentially.
Since creating a promise to download a file attempts to download the file immediately, one can't create all the promises at once if one intends to download them one at a time. Otherwise, the sequence might look a somewhat simpler.

Stop code execution when rejecting promise

I have the following promise-returning function:
function createJourney() {
return new Promise((resolve, reject) => {
// code ...
doOperation((err, data) {
// code ...
return reject('We need to exit now!')
});
// ---> Why is code stil executing here? <---
})
}
Why is the code executing below the reject? When rejecting, I want to stop the execution of the createJourney function.
I am using Bluebird promise.
Imagine
return new Promise((resolve, reject) => {
doThing1()
doOperation(..);
doThing2()
})
there is no reason why doThing2() should not be executed depending on what happens inside doOperation()
doOperation will probably start an asynchronous operation and doThing2 will be called long before reject is called in your example.

Categories