Error with JavaScript promise and then condition - javascript

I'm learning JavaScript promises and then, and am confused with this error using Node.js.
I would like dostart() to wait until nonblocking sleep is finished, and then return "Resolved" to the main function when it is done.
I get this error:
dostart().then(value => {
---------^
TypeError: Cannot read properties of undefined (reading 'then')
Help appreciated :)
function nonBlockingSleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function dostart() {
console.log("Hello2");
nonBlockingSleep(2000).then(() => {
console.log("Done");
return Promise.resolve("Resolved");
});
}
dostart().then(value => {
// main function - I'd like console.log to show "Resolved" when dostart() is finished
console.log(value);
})

dostart isn't async and doesn't return the promise from then, so you can't try to consume that promise where you call it.
You have at least a couple of options.
The simple one in this particular case is to return the promise from then:
function nonBlockingSleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function dostart() {
console.log("Hello2");
// *** Return the promise
return nonBlockingSleep(2000).then(() => {
console.log("Done");
// *** There's no need for `Promise.resolve` here, `then`
// _always_ returns a promise, and will use the return value
// here to fulfill it. (If you return a promise instead, it will
// resolve its promise to the promise you return instead of
// fulfilling its promise.)
return "Fulfilled";
});
}
dostart().then((value) => {
console.log(value);
});
Your other option is to use async/await:
function nonBlockingSleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// *** Make `dostart` an `async` function
async function dostart() {
console.log("Hello2");
// *** `await` the promise from `nonBlockingSleep`
await nonBlockingSleep(2000);
console.log("Done");
// *** Return the fulfillment value for this function's promise
return "Fulfilled";
}
dostart().then((value) => {
console.log(value);
});
Side note: I changed "Resolved" to "Fulfilled" because we're fulfilling the promise in that particular case. It's common, but incorrect, to use "resolve" for "fulfill." It's true that fulfilling a promise resolves it, but the converse isn't true — resolving a promise doesn't always fulfill it. It might leave it pending, or even eventually reject it, if you resolve it to another promise. I've written up promise terminology in this blog post.

since dostart returns nothing you can fix it like this:
function dostart() {
console.log("Hello2");
return nonBlockingSleep(2000).then(() => { console.log("Done"); return Promise.resolve("Resolved"); });
}
or use keyword async
async function dostart() {
console.log("Hello2");
return await nonBlockingSleep(2000).then(() => { console.log("Done"); return Promise.resolve("Resolved"); });
}

Related

promise not work as expected misunderstanding?

fetch("https://jsonplaceholder.typicode.com/posts").then(
(response) => {
console.log(response.json()); //why the data not logged and promise is logged
}
);
but work if i writte
let response = fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json())
.then((data) => console.log(data));
why the data is not logged and promise is logged in the first code?
response.json() is returning a promise, so you need to await it, either by doing what you did in the second example, or using async and await:
fetch("https://jsonplaceholder.typicode.com/posts").then(
async (response) => {
console.log(await response.json());
}
);
js fiddle: https://jsfiddle.net/hy15ve68/
This is only to demonstrate how you would get the data out of the promise (don't do it this way):
fetch("https://jsonplaceholder.typicode.com/posts").then(
(response) => {
// response.json() returns a Promise which we can call
// .then() on.
response.json().then(console.log)
}
);
The second code is actually a shortened version of:
let response = fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => {
return response.json()
})
// .then() refers to the promise returned by response.json()
.then((data) => {
return console.log(data)
});
This is the correct way of chaining Promises.
As you can see, we return the Promise returned by response.json() and then call .then() on it.
The good thing about chaining Promises is that synchronous values (like numbers, strings etc.) get wrapped in a Promise so you can still call .then() on it:
let dummy_promise = (new Promise(resolve => {
resolve(1)
}))
dummy_promise.then(value => {
console.log("im expecting 1", value)
return 2;
})
.then(value => {
console.log("im expecting 2", value)
return 3;
})
.then(value => {
console.log("im expecting 3", value)
})
.then(value => {
console.log("im expecting undefined because we haven't returned anything in the previous .then() block!", value)
})
A little background information:
You cannot expect the result of a Promise to be available immediately.
This is why you use .then() - it's a way of saying "call this function when the value IS available".
When you console.log(response.json()) you get the Promise object, but not it's resolved value.
Note: Even if the Promise itself was resolved, response.json() will continue to give you the Promise object itself.
You can still call .then() on it and your function will be called with the resolved value.
I hope this small example shows what I mean:
// returns a Promise that gets resolved to "hello!" after
// 100 ms (milliseconds)
function something() {
return new Promise((resolve) => {
setTimeout(() => {
resolve("hello!")
}, 100)
})
}
// call something() and store the promise in promise_object
let promise_object = something()
console.log("Installing .then() handlers")
// call console.log twice after promise is resolved
// (means they will be called in about 100ms - when the promise is resolved)
promise_object.then(console.log)
promise_object.then(console.log)
console.log("Installed .then() handlers")
// wait for 1 second, then print promise_object
setTimeout(() => {
console.log("1 second has passed")
// at this point the promise returned by something() surely must be
// resolved
console.log(promise_object) // Still prints Promise {} and not "hello!" - that's intended behavior
// gets called without delay because promise is already resolved!
promise_object.then(console.log)
}, 1000)
Output:

Promises: how to return a promise which has a otherwise error handler

I am using tableau js api and calling worksheet.applyFilterAsync & workbook.changeParameterValueAsync methods based on certain conditions. These methods have an otherwise error handler.
I am also in between the executing flow return my custom promise like
return new Promise((resolve, reject) => {
resolve(true)
});
the issue is when i am returning promise and not changeParameterValueAsync or worksheet.applyFilterAsync i am getting an error
TypeError: undefined is not a function (near '...}).otherwise(function (err) {...')
looks like the promise i return does not have a otherwise error handler?
relevant code is as follows
const applyStep = (step) => {
if(step.step_type == 'filter') {
return worksheet.applyFilterAsync(step.name, values, 'replace');
} else if(step.step_type == 'parameter') {
return workbook.changeParameterValueAsync(`${step.name}`, value);
} else {
return new Promise((resolve, reject) => {
resolve(true)
});
}
};
const applyFilters = (counter) => {
return new Promise((resolve, reject) => {
let filter = filters[counter];
if(filter) {
applyStep(filter).then(promiseTimeout(filter.delay)).then(() => resolve(applyFilters(++counter))).otherwise(function(err) {
reject(err);
});
} else {
resolve(true);
}
});
};
I need the otherwise error handler to handle error returned by tableau api methods but it does not work in cases where i am returning my custom promise, may be because the promise i am returning does not have an otherwise error handler?
How can address this any help in fixing this will be really great, Thanks.
From the tableau docs:
otherwise(errback: Function) Promise Registers a rejection handler. Shortcut for then(null, errback).
So in your code, you could do
applyStep(filter)
.then(promiseTimeout(filter.delay))
.then(() => resolve(applyFilters(++counter)))
.then(null, function(err) {
reject(err);
});
That is instead of .otherwise(err) you can use .then(null, err).
The issue is that you are currently mixing two different types of promises. The tableau Promise and the standard JavaScript Promise. These two have different methods which currently results in issues. You can pass a tableau Promise to Promise.resolve() to convert it into a standard JavaScript Promise.
return Promise.resolve(worksheet.applyFilterAsync(step.name, values, 'replace'));
When working with the promise you now have to work with then(), catch() and finally() instead of then(), otherwise() and always().

How to create a function returning a promise that resolves after inner promise resolves

I am trying to create a wrapper function that returns a Promise object which will resolve after an inner Promise resolves. Basically, something like this:
function wrapper() {
return new Promise((resolve, reject) => {
MyApi.asyncCall()
.then(data => {
// do this first, then resolve outer promise
resolve();
})
.catch(e => {
reject();
});
});
}
The reason I need to do this is I have an interface that accepts a promise argument, then triggers an action once that promise resolves. I need to make an async call, then do something with the response, then trigger the interface.
Edit
Explanation of purpose: I need to perform actions with the response of the inner promise BEFORE resolving the outer promise. The interface accepts an unresolved promise object, and then mounts a component when it is resolved. If I pass the inner promise it will mount before the actions on the data response are performed.
Basically — you doing it right. You are resolve \ reject promise when you need.
But you can do it easily — simply return original promise:
function wrapper() {
return MyApi.asyncCall()
}
So your returning promise, which resolves or rejectes when MyApi.asyncCall resolves or rejects.
If you need to do some staff inside you can do it inside this promise:
function wrapper() {
return MyApi.asyncCall()
.then((data) => {
doSomeOtherStaff(data)
return data
})
}
Note that if you need data outside — you have to return it from your then callback inside.
Can you explain why you need the wrapper promise exactly?
MyApi.asyncCall()
.then(data => {
// do this first, then resolve outer promise
resolve();
})
.catch(e => {
reject();
});
is a promise itself, and it should be enough to just return it.
You can use async await for the first promise and after getting data of the first promise then return the second promise :
function wrapper(){
return new Promise( async (resolve,reject)=>{
let myFirstPromise = await firestPromise();
// do your stuff here
console.log(myFirstPromise)
return resolve('Second Promise');
});
}
function firestPromise(){
return new Promise( (resolve,reject)=>{
return resolve('First Promise');
});
}

Adding a promise to sequence if condition is met

I have a Promise, Promise A, which 'checks' some condition asynchronously. It is always resolved (no rejection), so only then is necessary. The resolve is either true or false (the aforementioned condition).
If the condition is not met (resolve(false)) in the first promise, a second promise, Final Promise is then executed. However, if the initial condition was met (resolve(true)), an additional promise is executed 'between the two': Promise B.
Because Final Promise produces two callbacks (then and catch), I am trying to avoid code duplication. This is what I'm using, which works, but the final callbacks are duplicated.
promiseA().then((condition) => {
if (condition) {
promiseB().then(finalPromise().then(() => {
console.log('final promise success');
}).catch(() => {
console.log('final promise failure');
}));
} else {
finalPromise().then(() => {
console.log('final promise success');
}).catch(() => {
console.log('final promise failure');
});
}
});
I tried the following, but it doesn't work: Final Promise is being executed before Promise B.
promiseA().then((condition) => {
if (condition) {
return promiseB();
}
}).then(finalPromise().then(() => {
console.log('final promise success');
}).catch(() => {
console.log('final promise failure');
}));
Additionally, I tried using the following, but then Final Promise wouldn't be executed after Promise B:
promiseA().then((condition) => {
if (condition) {
return promiseB();
}
}).then(finalPromise).then(() => {
console.log('final promise success');
}).catch(() => {
console.log('final promise failure');
});
I'm guessing I could store everything that is inside the else branch (in the first snippet) into a variable and use it for both cases. I'm wondering if there's a solution built into the Promise API.
I figured it out. The third snippet works, but I needed to 'manually' resolve Promise B to continue the chain (I learned something new!).
function promiseB() {
return new Promise((resolve) => {
console.log('Promise B execution');
resolve(); // adding this solved it
});
}
The queue snippet:
promiseA().then((condition) => {
if (condition) {
return promiseB();
}
}).then(finalPromise).then(() => {
console.log('final promise success');
}).catch(() => {
console.log('final promise failure');
});

How to skip promise in a chain

I am working in a nodejs project and want to skip promise in a chain. Below is my code. In the first promise block, it will resolve a value {success: true}. On the second block I want to check the value of success, if true I want to return the value to the called and skip the rest of the promises in this chain; while continue the chain if the value is false. I know I can throw an error or reject it on the second block but I have to handle error case which it is not an error case. So how can I achieve this in promise chain? I need a solution without bring any other 3rd party library.
new Promise((resolve, reject)=>{
resolve({success:true});
}).then((value)=>{
console.log('second block:', value);
if(value.success){
//skip the rest of promise in this chain and return the value to caller
return value;
}else{
//do something else and continue next promise
}
}).then((value)=>{
console.log('3rd block:', value);
});
Simply nest the part of the chain you want to skip (the remainder in your case):
new Promise(resolve => resolve({success:true}))
.then(value => {
console.log('second block:', value);
if (value.success) {
//skip the rest of this chain and return the value to caller
return value;
}
//do something else and continue
return somethingElse().then(value => {
console.log('3rd block:', value);
return value;
});
}).then(value => {
//The caller's chain would continue here whether 3rd block is skipped or not
console.log('final block:', value);
return value;
});
If you don't like the idea of nesting, you can factor the remainder of your chain into a separate function:
// give this a more meaningful name
function theRestOfThePromiseChain(inputValue) {
//do something else and continue next promise
console.log('3rd block:', value);
return nextStepIntheProcess()
.then(() => { ... });
}
function originalFunctionThatContainsThePromise() {
return Promise.resolve({success:true})
.then((value)=>{
console.log('second block:', value);
if(value.success){
//skip the rest of promise in this chain and return the value to caller
return value;
}
return theRestOfThePromiseChain(value);
});
}
Aside from that, there isn't really a way to stop a promise mid-stream.
You could also continue to throw the error down the chain to a final catch block if you like.
const test = () => {
throw new Error('boo');
}
const errorHandler = (e) => { throw e };
Promise.resolve()
.then(console.log('then1'))
.then(() => test())
.then(f => console.log('then2'), errorHandler)
.then(f => console.log('then3'), errorHandler)
.catch((err) => console.log('caught', err));
// whoo
// then1
// caught Error: boo

Categories

Resources