I need to chain my callback function in .then() resolve method and promise. I am stuck on this problem since yesterday and couldn't find the solution anywhere. Can someone help me out?
Here's my code:
class Pact {
constructor(executor) {
this.state = 'pending';
this.value = undefined;
this.handlers = [];
const resolve = (value) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.handlers.forEach(handler => handler(value));
}
};
const reject = (reason) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
this.handlers.forEach(handler => handler(reason));
}
};
executor(resolve, reject)
.then();
}
catch (onRejected) {
if (this.state == 'rejected') {
onRejected(this.value);
}
else {
this.handlers.push(onRejected);
}
}
then(onFulfilled) {
if (this.state === 'fulfilled') {
onFulfilled(this.value);
}
else {
this.handlers.push(onFulfilled);
}
}
}
module.exports = Pact;
I have tried every thing on youtube and google. Even asked Chat GPT about it but it gave zero response.
Related
I'm learning JavaScript, and I decided that an excelent chalenge would be to implement a custom Promise class in JavaScript. I managed to implement the method then, and it works just fine, but I'm having difficulties with the error handling and the method catch. Here is my code for the Promise class (in a module called Promise.mjs):
export default class _Promise {
constructor(executor) {
if (executor && executor instanceof Function) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error);
}
}
}
resolve() {
if (this.callback && this.callback instanceof Function) {
return this.callback(...arguments);
}
}
reject(error) {
if (this.errorCallback && this.errorCallback instanceof Function) {
return this.errorCallback(error);
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
}
then(callback) {
this.callback = callback;
return this;
}
catch(errorCallback) {
this.errorCallback = errorCallback;
return this;
}
}
When I import and use this class in the following code, all the then() clauses run as according, and I get the desired result in the console:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
But, when I add an invalid argument to the sum() function, i.e. not a number, the reject() method runs, but it don't stop the then() chain, as should be, and we also get an exception. This can be seen from the following code:
import _Promise from "./Promise.mjs";
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, '5').then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
console.timeEnd('codeExecution');
});
});
}).catch(function (error) {
console.log(error);
});
Also, if I catch an error in nested then() methods, the outer catch() doesn't notice this and I get an exception again. The goal is to implement a lightweight functional version of Promises, but not necessarily with all its functionality. Could you help me?
The problem in your code is that your sum function calls both the reject and the resolve functions. There's no handling in the sum function that will cause it not to call the resolve function at the end, and there is nothing in your _Promise that blocks this behavior.
You have 2 options to fix this.
Option 1 would be if you want your _Promise to act like a real Promise you will need to manage a state and once a promise got to a final state stop calling the callback or errorCallback.
Option 2 would be to prevent from calling both reject and resolve in the function calling the _Promise, in this case, the sum function.
With the comments that you guys provide me, I was able to improve the code and correct the errors mentioned, as shown below. Now, I would like you to give me suggestions on how to proceed and improve the code. Thanks. (The code can also be found on github).
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
function _Promise(executor) {
let state = PENDING;
let callOnFulfilled = [];
let callOnRejected = undefined;;
function resolve(...args) {
if (!state) {
state = FULFILLED;
}
resolveCallbacks(...args);
};
function reject(error) {
state = REJECTED;
if (callOnRejected && (callOnRejected instanceof Function)) {
callOnRejected(error);
callOnRejected = undefined;
callOnFulfilled = [];
} else {
throw `Unhandled Promise Rejection\n\tError: ${error}`;
}
};
function resolveCallbacks(...value) {
if (state !== REJECTED) {
let callback = undefined;
do {
callback = callOnFulfilled.shift();
if (callback && (callback instanceof Function)) {
const result = callback(...value);
if (result instanceof _Promise) {
result.then(resolveCallbacks, reject);
return;
} else {
value = [result];
}
}
} while (callback);
}
};
if (executor && (executor instanceof Function)) {
executor(resolve, reject);
}
this.then = function (onFulfilled, onRejected) {
if (onFulfilled) {
callOnFulfilled.push(onFulfilled);
if (state === FULFILLED) {
resolveCallbacks();
}
}
if (onRejected && !callOnRejected) {
callOnRejected = onRejected;
}
return this;
};
this.catch = function (onRejected) {
return this.then(undefined, onRejected);
};
}
function sum(...args) {
let total = 0;
return new _Promise(function (resolve, reject) {
setTimeout(function () {
for (const arg of args) {
if (typeof arg !== 'number') {
reject(`Invalid argument: ${arg}`);
}
total += arg;
}
resolve(total);
}, 500);
});
}
console.time('codeExecution');
sum(1, 3, 5).then(function (a) {
console.log(a);
return sum(2, 4).then(function (b) {
console.log(b);
return sum(a, b).then(function (result) {
console.log(result);
return 25;
});
}).then(function (value) {
console.log(value);
console.timeEnd('codeExecution');
});
}).catch(function (error) {
console.log(error);
});
Sometimes I'd like to use the RxJS operators to manipulate an endless asynchronous iterable without buffering the values. It is easy to turn an iterable into an Observable. Are there downsides in the following approach to turn an Observable into an asynchronous iterable?
const iterable = async function* (observable) {
let buffer = [],
resolve,
reject;
const subscription = observable.subscribe({
next: value => {
if (resolve) {
resolve(value);
resolve = reject = undefined;
} else {
buffer.push(Promise.resolve(value));
}
},
error: e => {
if (reject) {
reject(e);
resolve = reject = undefined;
}
},
complete: () => {},
});
while (!subscription.isStopped || buffer.length) {
yield buffer.shift() ||
new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
}
subscription.unsubscribe();
};
Codepen demo
Here is an alternative implementation of the iterable as an Observer (without using a generator).
class IterableObserver {
constructor(observable) {
this.buffer = [];
this.resolve = undefined;
this.reject = undefined;
this.isStopped = false;
observable && observable.subscribe(this);
}
[Symbol.asyncIterator]() {
const t = this;
return {
next: async () => {
if (!t.isStopped || t.buffer.length) {
if (t.buffer.length) {
return {
value: t.buffer.shift(),
};
} else {
return new Promise((_resolve, _reject) => {
t.resolve = _resolve;
t.reject = _reject;
});
}
} else {
return { done: true };
}
},
};
}
next(value) {
if (this.resolve) {
this.resolve({ value });
this.resolve = this.reject = undefined;
} else {
this.buffer.push(value);
}
}
error(e) {
this.isStopped = true;
if (this.reject) {
this.reject(e);
this.resolve = this.reject = undefined;
}
}
complete() {
this.isStopped = true;
}
}
The benefit from this was questioned. Let's say you have an API which provides you with an asynchronous iterable of text file lines through the function makeTextFileLineIterator and you would need to provide an asynchronous iterable of the first N lines in uppercase for your client. How would you do that? With the RxJS operators and the iterable conversion it would be easy:
const lineIterator = makeTextFileLineIterator('https://cdn.skypack.dev/rxjs#^7.1.0/operators?min');
const upperCaseLines = new IterableObserver(from(lineIterator).pipe(
take(6),
map(line=>line.toLocaleUpperCase())),
);
Codepen demo
can someone help me solve this problem. I dont understand why console log and value of it after evaluate different. As you can see this pic below key SeqNo in console.log has no value but it actually has it
This is my function in component in Angular will return array and will be parameter for other function has above console.log
async prepareDetailForSave(allModelSalesTargetDetail) {
const salesTargetDetailForSave: any = [];
for (const detail of allModelSalesTargetDetail) {
await new Promise ((resolve, reject) => {
if (detail.SeqNo === '') {
if (detail.Money !== 0) {
detail.RSeqNo = this.objMaster.SeqNo;
detail.CreateDate = UtilsService.getCurrentDate();
detail.ModifiedDate = UtilsService.getCurrentDate();
if (this.global.connectionStatus === 'offline') {
detail.SeqNo = UtilsService.getLocalSeqno().toString();
} else {
this.jamService.getInvoiceCode('sales-target-detail').subscribe((res: any) => {
detail.SeqNo = res['seqno'];
});
}
salesTargetDetailForSave.push(detail);
}
} else {
salesTargetDetailForSave.push(detail);
}
resolve();
});
}
return salesTargetDetailForSave;
}
I have a function that is called that must return a response to a server. Inside this function are two await function calls that are nested. To track error handling, I added try/catch blocks. Is there a way to avoid having nested try catch blocks to track all cases where the function might fail so I can send back an error server response?
Here's my function, it queries for a user's unique device id's and sends a push notification to each one. If a token becomes invalid, then I delete it from my database:
function findUserDevices(uid: string, message) {
collectionData(fb.firestore().collection('devices').where('userId', '==', uid)).pipe(
filter((userDevices) => userDevices && userDevices.length > 0),
take(1)
).subscribe( async (devices: any) => {
var userDeviceTokens: string[] = devices.map((device: any) => device.token);
if (userDeviceTokens !== undefined && userDeviceTokens.length != 0) {
try {
message['tokens'] = userDeviceTokens;
const pushResponse = await admin.messsaging().sendMulticast(message);
if (pushResponse.failureCount > 0) {
const failedTokens = [];
pushResponse.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(userDeviceTokens[idx]);
}
});
failedTokens.forEach( async (token) => {
var tokenInstanceID = token.split(':')[0];
try {
await deleteOldToken(tokenInstanceID);
console.log(`Token ${tokenInstanceID} deleted`)
} catch {
return res.status(500).send("err");
}
})
return res.status(200).send("ok");
} else {
return res.status(200).send("ok");
}
} catch {
return res.status(500).send("err");
}
} else {
return res.status(200).send("ok");
}
})
}
It just feels a bit excessive with all the returns I must have. Where can I improve?
EDIT, broke apart code into three blocks to prevent arrow coding
function findUserDevices(uid: string, message) {
collectionData(fb.firestore().collection('devices').where('userId', '==', uid)).pipe(
filter((userDevices) => userDevices && userDevices.length > 0),
take(1)
).subscribe(async (devices: any) => {
var userDeviceTokens: string[] = devices.map((device: any) => device.token);
if (userDeviceTokens !== undefined && userDeviceTokens.length != 0) {
try {
message['tokens'] = userDeviceTokens;
const response = await admin.messaging().sendMulticast(message);
const oldTokensArray = checkOldTokens(response, userDeviceTokens);
if (oldTokensArray.length > 0) {
await deleteOldTokens(oldTokensArray);
return res.status(200).send("ok");
} else {
return res.status(200).send("ok");
}
} catch (err) {
return res.status(500).send(err);
}
} else {
return res.status(200).send("ok");
}
})
}
function checkOldTokens(response, userDeviceTokens) {
if (response.failureCount > 0) {
const failedTokens = [];
response.responses.forEach((resp, idx) => {
if (!resp.success) {
failedTokens.push(userDeviceTokens[idx]);
}
});
return failedTokens;
} else {
return [];
}
}
async function deleteOldTokens(tokenArray) {
for (const token of tokenArray) {
await fb.firestore().collection('devices').doc(token).delete();
}
}
How would I write the following without jQuery?
var dfd = $.Deferred()
dfd.done(done)
dfd.resolve()
function done() {
console.log('done')
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Use native promises:
Promise
.resolve()
.then(done);
function done() {
console.log('done')
}
Update
Without the chain:
let op = Promise.resolve();
op.then(done);
function done() {
console.log('done')
}
function Deferred (){
let res,rej,p = new Promise((a,b)=>(res = a, rej = b));
p.resolve = res;
p.reject = rej;
return p;
}
You just need to expose resolve and reject to make it work.
The problem with using native promises is that the resolve and reject handler is provided in the callback, so if you try and call them before they are actually assigned.
In my opinion it's more robust to just implement a deferred yourself for example:
function deferred() {
let thens = []
let catches = []
let status
let resolvedValue
let rejectedError
return {
resolve: value => {
status = 'resolved'
resolvedValue = value
thens.forEach(t => t(value))
thens = [] // Avoid memleaks.
},
reject: error => {
status = 'rejected'
rejectedError = error
catches.forEach(c => c(error))
catches = [] // Avoid memleaks.
},
then: cb => {
if (status === 'resolved') {
cb(resolvedValue)
} else {
thens.unshift(cb)
}
},
catch: cb => {
if (status === 'rejected') {
cb(rejectedError)
} else {
catches.unshift(cb)
}
},
}
}
const d = deferred()
setTimeout(() => {
d.resolve('good')
}, 1000)
// Will be called after 1s
d.then(value => console.log('#1 resolved!', value))
setTimeout(() => {
// Will be called after 3s and executed right away as it's already resolved
d.then(value => console.log('#2 resolved!', value))
}, 3000)