Basically, i'm so confused with the Promises. Because, I'm still new to moved on from Callback method for handling Asynchronous.
So, i have a code like these
const Search = function(name) { //
return new Promise((resolve, reject) => { //
let o = [{
"name": "Apple",
"quantity": 10
},
{
"name": "Grape",
"quantity": 5
}
]
let result = o.filter((n) => n.name == name)
if (result.length < 1) {
reject()
return
}
resolve(result[0])
})
}
const Buy = function(data, qty) {
return new Promise((resolve, reject) => {
let o = {
name,
quantity
} = data
o.quantity = parseInt(o.quantity) - qty
if (o.quantity < 0) {
throw new Error("Oops. Quantity is Empty!")
}
resolve(o)
})
}
const Result = function(test) {
console.log(test)
}
The main purpose, how i can input a value into a qty arguments on Buy function?
I was do something like this, but the result is not expected what i want. I'm sure, my code has something missing but i don't know.
Search("Apple")
.then(Buy, 4)
.then(Result)
The result is :
{ name: 'Apple', quantity: NaN }
Main goal is :
{ name: 'Apple', quantity: 6 }
Anyway thanks :)
Search("Apple")
.then(function(result){return Buy(result, 4)})
.then(Result)
You were trying to pass Buy directly into .then, but the function in .then always gets passed only 1 argument. So you can call and return Buy in an anonymous function, where you can apply yourself as many arguments you want.
You can take advantage of scope.
function executeSearch() {
Search("Apple").then(function (searchResult) {
// feel free to use the result of the first promise
// as input parameters to the second if desired
const name = searchResult.name;
const quantity = searchResult.quantity;
Buy(searchResult, 4).then(Result);
});
}
This is similar to the other answer but demonstrates that you can use the result of the first promise in any number of ways to execute the second promise.
The then method accepts a function, so what you can do is change your 'buy' to the following:
const Buy = function(qty) {
return function(data){
return new Promise((resolve, reject) => {
let o = {
name,
quantity
} = data
o.quantity = parseInt(o.quantity) - qty
if (o.quantity < 0) {
throw new Error("Oops. Quantity is Empty!")
}
resolve(o)
})
}
}
Then you can use it like:
Search("Apple")
.then(Buy(4))
.then(Result)
Related
I have an array of object, I want to add key in my specifi object of array when Id is matched. I have tried this:
this.data.forEach(value => {
if (value.Id === attachmentDataId) {
AttachmentTypeId: this.attachmentRecord.AttachmentType;
}
});
But it's not working and it's not giving any error also
Try this out :
let data = [{ id: 1 }, { id: 5 }];
const attachmentDataId = 5;
const attachmentRecord = { AttachmentType: "AttachmentType" };
data.forEach(value => {
if (value.id === attachmentDataId) {
value.AttachmentTypeId = attachmentRecord.AttachmentType;
}
});
The stackblitz example: https://stackblitz.com/edit/js-nrhouh
You could use the index parameter of forEach function to access the specific object of the array.
this.data.forEach((value, i) => {
if (value.Id === attachmentDataId) {
this.data[i] = {
...this.data[i],
AttachmentTypeId: this.attachmentRecord.AttachmentType
};
}
});
Inside the if block, you could also instead do
this.data[i]['AttachmentTypeId'] = this.attachmentRecord.AttachmentType;
I just find using the spread operator cleaner.
use javascript map() method.
Map() return a new array, it takes a callback, iterates on each element in that array
const updatedData = data.map(res => {
if(res.id === attachmentDataId) {
res.AttachmentTypeId = attachmentRecord.AttachmentType;
}
return res
})
I'm new to react and as well to the terms of functional, imperative, declarative. And I get to know that pure function is easy to test. I am self taught to program with Javascript. So far, it is working but my goal is to learn to write clean and maintainable code.
my question is the method addProductToSaleList below is bad and untestable because it is imperative? and how can I do it differently.
class SaleComponent extends React.Component {
addProductToSaleList = (values, dispatch, props) => {
//filter product from productList
const productFound = props.productList.filter(product => {
if (values.productCode === product.code.toString()) {
return product
}
return undefined
})[0]
if (productFound) {
// filter sale list to check if there is already product in the list.
const detailFound = props.saleItem.details.filter(detail => {
if (productFound.name === detail.product) {
return detail
}
return undefined
})[0]
// if it is exist just increment the qty
if (detailFound) {
const { sub_total, ...rest } = detailFound
props.dispatcher('UPDATE_SALEDETAIL_ASYNC', {
...rest,
qty: parseInt(detailFound.qty, 10) + 1
})
// if it is not exist add new one
} else {
props.dispatcher('ADD_SALEDETAIL_ASYNC', {
product: productFound.id,
price: productFound.price,
qty: 1
})
}
} else {
alert('The product code you add is not exist in product list');
}
}
render() {
// Render saleList
}
}
I belive this question should go to Code Review, but I will give it a shot. Part of the code can be improved
const productFound = props.productList.filter(product => {
if (values.productCode === product.code.toString()) {
return product
}
return undefined
})[0]
First, filter function receives a callback and for each item that callback will be executed. If the callback returns a value interpreted as true, it will return the item in the new array the function will build. Otherwise, it will skip that item. Assuming you're trying to find one item in the code, you could use the function find which will return you that element directly (no need for [0]), or undefined if that item is not found. So your code could be rewrite to
const productFound = props.productList.find(product => values.productCode === product.code.toString());
Note: No IE support.
Then, if the value was not found, you could just alert and do an early return. (You might also want to handle errors differently, with a better format than plain alert).
The code would look like
if (!productFound) {
alert('The product code you add is not exist in product list');
return;
}
// rest of the function
in order to find details, you can use find method as well
const detailFound = props.saleItem.details.find(detail => productFound.name === detail.product);
and then just call the rest of the code
// if it is exist just increment the qty
if (detailFound) {
const { sub_total, ...rest } = detailFound
props.dispatcher('UPDATE_SALEDETAIL_ASYNC', {
...rest,
qty: parseInt(detailFound.qty, 10) + 1
})
// if it is not exist add new one
} else {
props.dispatcher('ADD_SALEDETAIL_ASYNC', {
product: productFound.id,
price: productFound.price,
qty: 1
})
}
Another improvement:
You're receiving a dispatch function as a parameter, but you're not using it. So you could remove it from function's declaration
(values, props) => { ... }
And you could split the last part into two different functions, something like
const getAction = details => `${detailFound ? 'UPDATE' : 'ADD'}_SALEDETAIL_ASYNC`;
const getObject = (details, productFound) => {
if (!details) {
return {
product: productFound.id,
price: productFound.price,
qty: 1
};
}
const { sub_total, ...rest } = detailFound;
return {
...rest,
qty: parseInt(detailFound.qty, 10) + 1
};
}
and then just call
props.dispatcher(getAction(details), getObject(details, productFound));
The end result would look like
addProductToSaleList = (values, props) => {
//filter product from productList
const productFound = props.productList.find(product => values.productCode === product.code.toString());
if (!productFound) {
alert('The product code you add is not exist in product list');
return;
}
// filter sale list to check if there is already product in the list.
const detailFound = props.saleItem.details.find(detail => productFound.name === detail.product);
const getAction = details => `${details ? 'UPDATE' : 'ADD'}_SALEDETAIL_ASYNC`;
const getObject = (details, productFound) => {
if (!details) {
return {
product: productFound.id,
price: productFound.price,
qty: 1
};
}
const { sub_total, ...rest } = details;
return {
...rest,
qty: parseInt(details.qty, 10) + 1
};
}
props.dispatcher(getAction(details), getObject(details, productFound));
}
my question is the method addProductToSaleList below is bad and
untestable because it is imperative
Well your code is testable, there are no external dependencies. So you could pass mocked values and props and add unit tests to that. That means, passing a fake values and props (they are just plain js object) and make assertions over that.
For instance:
You could mock dispatcher function and given the fake values in productList and saleItem.details you could see if dispatcher is called with the proper values. You should test different combinations of that
Mock alert function (Again, I would use another UI approach) and verify it is called, and that no other code is called (asserting that your fake dispatcher is not called). Something like this:
let actionToAssert;
let objectToAssert;
let values = { productCode: 'somecode' };
let props = {
productList: // your item listm with id and price, name, etc,
saleItem: {
details: // your details array here
}
dispatcher: (action, newObject) => {
actionToAssert = action;
objectToAssert = newObject;
}
}
addProductToSaleList(values, props); // make here assertions over actionToAssert and objectToAssert
Here is my code :
Search.prototype.makeQuery = function (data) {
let result = {};
if (data.orderId) {
result["order_id"] = data.orderId;
}
if (data.userMobileNumber) {
result["user.Number"] = {$regex : data.userMobileNumber}
}
if (data.userFullName) {
result["user.Name"] = {$regex: data.userFullName}
}
return result;
};
All I want is finding better way to optimize my code and reduce if condition in my code. Is there any suggestion ?
You can avoid the typing of if when you wrap it into a function and the typing of data with destructuring.
The advantage of wrapping the if in this case into a function is that you can simply test it, it is reusable and easy to read
Code
Search.prototype.makeQuery = function (data) {
let result = {}
let {orderId, userMobileNumber, userFullName} = data
setObjectValue(orderId, result, "order_id", orderId)
setObjectValue(userMobileNumber, result, "user.Number", {$regex : userMobileNumber})
setObjectValue(userFullName, result, "user.Name", {$regex: userFullName})
return result;
}
function setObjectValue(condition, object, key, value) {
if(condition) {
object[key] = value
}
}
Working Example
function makeQuery (data) {
let result = {}
let {orderId, userMobileNumber, userFullName} = data
setObjectValue(orderId, result, "order_id", orderId)
setObjectValue(userMobileNumber, result, "user.Number", {$regex : userMobileNumber})
setObjectValue(userFullName, result, "user.Name", {$regex: userFullName})
return result;
}
function setObjectValue(condition, object, key, value) {
if(condition) {
object[key] = value
}
}
let data = {
orderId: 1,
userMobileNumber: "016875447895",
userFullName: "John Doe"
}
let query = makeQuery(data)
console.log(query)
A simpler way:
Search.prototype.makeQuery = function (data) {
let result = {};
data.orderId && (result["order_id"] = data.orderId);
data.userMobileNumber && (result["user.Number"] = {$regex : data.userMobileNumber});
data.userFullName && (result["user.Name"] = {$regex: data.userFullName});
return result;
};
Let's imagine you have many fields or you want to modify them, you would create a map. Right now, your code works and my solution is overkill but it may be useful in the future:
const interestingData = new Map()
//I tried to imitate your values.
// Use whatever function you want here as a callback.
// value is the value you request, the callback must return the value you want to set.
interestingData.set("order_id", value => value)
interestingData.set("user.Number", value => ({ $regevalue: value }))
interestingData.set("user.Name", value => ({ $regevalue: value }))
//Tgis is a Factory in case you need several search.
const makeSearch = fields => data => {
let result = {}
fields.forEach((callBack, field) => {
if (data[field])
result[field] = callBack(data[field])
})
return result
}
//Creating a searching function
const myResearch = makeSearch(interestingData)
//Fake examples
const data1 = {
order_id: 'ertyui',
"user.Number": "ertyuio",
"user.Name": "ertyuio",
azerr: 123456
}
const data2 = {
order_id: 'ertyui',
"user.Number": "ertyuio",
}
console.log(myResearch(data1))
console.log(myResearch(data2))
It is not simpler but it is more extensible, and when you have many parameters, it is going to be much faster on a big scale. It is also reusable. Hope that helps!
Not sure if you'd consider this as code optimization, but you can get rid of the if statements using Object.assign:
Search.prototype.makeQuery = function (data) {
return Object.assign({},
data.orderId && { order_id: data.orderId },
data.userMobileNumber && {
'user.Number': { $regex : data.userMobileNumber },
},
data.userFullName && {
'user.Name': { $regex : data.userFullName },
},
)
};
If you can use newer JS features (with a transpiler or otherwise), you could use Object rest/spread for a slightly more concise syntax:
Search.prototype.makeQuery = (data) => ({
...data.orderId && { order_id: data.orderId },
...data.userMobileNumber && {
'user.Number': { $regex : data.userMobileNumber },
},
...data.userFullName && {
user.Name': { $regex : data.userFullName },
},
});
Edit 1: note that all these are pure functions, no mutations are taking place whatsoever
This is how I'm doing it:
const Document = Parse.Object.extend('Document')
const query = new Parse.Query(Document)
let result = {}
query.get(id, {
success: function (object) {
result = object.toJSON()
console.log(result)
},
error: function (object, error) {
alert(`Error: ${error.code} ${error.message}`)
}
})
console.log(result)
return result
The first console.log(result) outputs the object:
Object {content: "trstrtrts", createdAt: "2016-01-17T11:20:30.694Z",
title: "Document 2", updatedAt: "2016-01-17T11:20:30.694Z", wordCount:
"3000"…}
But the second one returns nothing. What's the correct way of returning an object from a Parse query?
EDIT:
Based on Anon's answer I tried this:
store.js:
store.first = (id) => {
var query = new Parse.Query(Document)
return query.get(id)
}
export default store
main.js:
store.first(to.params.id).then((document) => {
console.log(document.toJSON())
return document.toJSON()
})
But I get the following error:
Uncaught TypeError: Object function ParsePromise() {
_classCallCheck(this, ParsePromise); this._resolved = false; this._rejected = false; this._resolvedCallbacks = [];
this._rejectedCallbacks = []; } has no method 'all'
The second
console.log(result)
take place before the first one.Query is an async operation.
The correct way of doing this is to use promises. For example you can do
function foo(id){
var Document = Parse.Object.extend('Document');
var query = new Parse.Query(Document);
return query.get(id);
}
and then use the function foo like this:
foo(objectID).then(function(object){
//do something with the object.
})
here is an example to show the async in js.
console.log('a');
setTimeOut(function(){console.log('b')},0);
console.log('c');
the order of the printing is
a
c
b
(we have time out 0 but the function of the timeout goes in the event loop and take place after the function done)
for more information you can read https://developer.mozilla.org/he/docs/Web/JavaScript/EventLoop
about the eventloop
I have a sequence of objects that I need to asynchronously modify by adding a property to each object:
[{ id: 1 }, { id: 2 }] => [{ id: 1, foo: 'bar' }, { id: 2, foo: 'bar' }]
The synchronous equivalent of this would be:
var xs = [{ id: 1 }, { id: 2 }];
// Warning: mutation!
xs.forEach(function (x) {
x.foo = 'bar';
});
var newXs = xs;
However, in my case I need to append the foo property asynchronously. I would like the end value to be a sequence of objects with the foo property added.
I came up with the following code to solve this problem. In this example I'm just adding a property to each object with a value of bar.
var xs = Rx.Observable.fromArray([{ id: 1 }, { id: 2 }]);
var propertyValues = xs
// Warning: mutation!
.flatMap(function (x) {
return Rx.Observable.return('bar');
});
var newXs =
.zip(propertyValues, function (x, propertyValue) {
// Append the property here
x.foo = propertyValue;
return x;
})
.toArray();
newXs.subscribe(function (y) { console.log(y); });
Is this the best way to solve my problem, or does Rx provide a better means for asynchronously mutating objects in a sequence? I'm looking for a cleaner solution because I have a deep tree that I need to mutate, and this code quickly becomes unweidly:
var xs = Rx.Observable.fromArray([{ id: 1, blocks: [ {} ] }, { id: 2, blocks: [ {} ] } ]);
var propertyValues = xs
// Warning: mutation!
.flatMap(function (x) {
return Rx.Observable.fromArray(x.blocks)
.flatMap(function (block) {
var blockS = Rx.Observable.return(block);
var propertyValues = blockS.flatMap(function (block) {
return Rx.Observable.return('bar');
});
return blockS.zip(propertyValues, function (block, propertyValue) {
block.foo = propertyValue;
return block;
});
})
.toArray();
});
xs
.zip(propertyValues, function (x, propertyValue) {
// Rewrite the property here
x.blocks = propertyValue;
return x;
})
.toArray()
.subscribe(function (newXs) { console.log(newXs); });
Perhaps I shouldn't be performing this mutation in the first place?
Is there a reason you need to create two separate Observables: one for the list you're updating and one for the resulting value?
If you simply perform a .map() over your original list, you should be able to asynchronously update the list and subscribe to the result:
// This is the function that generates the new property value
function getBlocks(x) { ... }
const updatedList$ = Rx.Observable.fromArray(originalList)
// What we're essentially doing here is scheduling work
// to be completed for each item
.map(x => Object.assign({}, x, { blocks: getBlocks(x)}))
.toArray();
// Finally we can wait for our updatedList$ observable to emit
// the modified list
updatedList$.subscribe(list => console.log(list));
To abstract this functionality, I created a helper function that will explicitly schedule work to occur for each item using setTimeout:
function asyncMap(xs, fn) {
return Rx.Observable.fromArray(xs)
.flatMap(x => {
return new Rx.Observable.create(observer => {
setTimeout(() => {
observer.onNext(fn(x));
observer.completed();
}, 0);
});
})
.toArray();
}
You can use this function to schedule work to be completed for each item:
function updateItem(x) {
return Object.assign({}, x, { blocks: getBlocks(x) }
}
var updatedList$ = asyncMap(originalList, updateItem);
updateList$.subscribe(newList => console.log(newList));