I have this code:
var c = function(address, abiJson){
var _ = this;
this.data = {
wallet: false,
account:{
address: false
},
contract:{
address: address
}
};
this.abi = $.getJSON(abiJson, function(abi){
_.data.abi = abi;
if(typeof web3 !== 'undefined'){
window.web3 = new Web3(web3.currentProvider);
window.cont = web3.eth.contract(abi).at(address);
}
});
this.getData = function(cb){
if(typeof _.data.abi !== 'undefined'){
_.updateData.done(() => {
cb(_.data);
});
}
else{
_.abi.then(() => {_.updateData
})
.done(() => {
cb(_.data);
});
}
}
this.updateData = Promise.all([
_.get('x'),
_.get('y')
])
.then(values => {
_.data.x.y= values[0];
_.data.x.z= values[1];
})
.then(() => {
Promise.all([
_.get('v', 1),
_.get('v', 2),
])
.then(values => {
_.data.y = values;
});
});
this.get = function(method, args){
return new Promise(function(resolve, reject) {
window.cont[method](args, function(error, result){
if(!error) resolve(result);
});
});
}
}
When I get the function _.get('x').then((x) => console.log (x)) outside the updateData function, I get all the data. But when I call the getData function I get errors for all the get functions _.get is not a function.
I just don't see where is my mistake. I'm new in js using Promise.
Here's a cut-down version of the question:
var C = function(address, abiJson){
var _ = this;
this.updateData = Promise.all([
_.get('x'),
_.get('y')
]);
this.get = function( arg){ return Promise.resolve( arg)};
}
var c = new C();
c.updateData.then( values => console.log(values));
Calling get inside the array initializer argument of the Promise.all call errors because get has yet to be added as a method of _ (which is set to this). Adding the get method before creating the array solves the immediate problem:
var C = function(address, abiJson){
var _ = this;
// add get method first:
this.get = function( arg){ return Promise.resolve( arg)};
this.updateData = Promise.all([
_.get('x'),
_.get('y')
]);
}
var c = new C();
c.updateData.then( values => console.log(values));
_ variable cannot be used that way. You can use it when you call it as a function, but cannot be used when you are creating an object.
You can bind the scope using bind for the functions. For arrow functions, the scope will be retained. Try the following code. I just replaced _ with this for all the functions and binded this to the callback of getJSON. If you use arrow function there also, bind will not be necessary.
var c = function(address, abiJson){
this.data = {
wallet: false,
account:{
address: false
},
contract:{
address: address
}
};
this.abi = $.getJSON(abiJson, function(abi){
this.data.abi = abi;
if(typeof web3 !== 'undefined'){
window.web3 = new Web3(web3.currentProvider);
window.cont = web3.eth.contract(abi).at(address);
}
}.bind(this));
this.getData = function(cb){
if(typeof this.data.abi !== 'undefined'){
this.updateData.done(() => {
cb(this.data);
});
}
else{
this.abi.then(() => {this.updateData
})
.done(() => {
cb(this.data);
});
}
}
this.get = function(method, args){
return new Promise(function(resolve, reject) {
window.cont[method](args, function(error, result){
if(!error) resolve(result);
});
});
}
this.updateData = Promise.all([
this.get('x'),
this.get('y')
])
.then(values => {
this.data.x.y= values[0];
this.data.x.z= values[1];
})
.then(() => {
Promise.all([
this.get('v', 1),
this.get('v', 2),
])
.then(values => {
this.data.y = values;
});
});
}
Related
I have two functions that, through promises, asynchronously return values from HTTP GET requests.
One needs to be called after the other because it depends on the result of the first one.
I trained to chain them using then but, although both GET requests are happening and bringing the expected JSON responses, I can't figure out how to assign the final return value to a variable that I can use outside both functions.
function getPageInfo(config, lastPageId) {
axios.get('http://localhost:8000/page/'+lastPageId, config)
.then(res => {
return res.data;
})
.catch(err => console.log(err));
}
function getUserPages(config) {
const userId = localStorage.getItem('userId');
axios.get('http://localhost:8000/user/'+userId, config)
.then(res => {
const pageInfo = getPageInfo(config, res.data.pages[res.data.pages.length - 1]);
console.log(pageInfo);
return pageInfo;
})
.catch(err => console.log(err));
}
Adjust your code to something like this:
// note the returns
function getPageInfo(config, lastPageId) {
return axios.get('http:.....')
}
function getUserPages(config) {
const userId = localStorage.getItem('userId');
return axios.get('http://lo....')
}
let res1;
let res2;
getPageInfo(...).then(result => {
// assign the result somewhere
res1 = result;
// return the next promese
return getUserPages(...);
}).then(result => {
res2 = result;
}).catch(error => {
console.log(error)
})
Must you use .then() and .catch()?
It's more modern to use async/await:
(async function () {
try {
const userId = localStorage.getItem('userId');
let res = await axios.get('http://localhost:8000/user/'+userId, config);
let lastPageId = res.data.pages[res.data.pages.length - 1]);
let pageInfo = await axios.get(http://localhost:8000/page/'+lastPageId, config);
console.log(pageInfo);
} catch (err) {
console.log(err);
}
})();
Below is a runnable example of Promise chaining using async/await:
function fetch(method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (xhr.status < 200 && xhr.status >= 300) {
reject(new Error(`HTTP Error ${xhr.status}: ${xhr.statusText}`));
return;
}
resolve(xhr.response);
};
xhr.onerror = function () {
reject(new Error(`HTTP Error ${xhr.status}: ${xhr.statusText}`));
};
xhr.send();
});
}
(async function () {
try {
let q = "webmap owner:Esri";
let searchText = await fetch("GET", `https://www.arcgis.com/sharing/rest/search?q=${encodeURIComponent(q)}&f=json`);
let search = JSON.parse(searchText);
let itemId = search.results[0].id;
console.log(`itemId: ${itemId}`);
let itemText = await fetch("GET", `https://www.arcgis.com/sharing/rest/content/items/${itemId}?f=json`);
let item = JSON.parse(itemText);
console.log(`title: ${item.title}`);
console.log(`snippet: ${item.snippet}`);
} catch (err) {
console.error(err.message);
}
})();
I am trying to export the value with instrument variable. however data is returning as [object Promise] than object. How can I assign module variable with the final result rather than the promise object.
var instruments = {
data: async () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." }
);
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols().then((data) => {
console.log('data');
return data;
}).catch((e) => {
console.log('error');
return {}
});
It looks like you want a singleton cache. Here is a basic implementation
cache.js
let data = {}
module.exports = {
getData: () => {
return data
},
setData: newData => {
data = newData
return
},
}
No need for async here. I would separate this code with the code that retrieves data.
fetchData.js
const cache = require('./cache')
const fetchData = () => {} // fetch data code here
fetchData().then(data => {
cache.setData(data)
})
try this
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." });
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols;
then import instrument method to call and then call
const instrument = require("./filepath");
instrument().then((data) => {
console.log('data');
}).catch((e) => {
console.log(e);
});
If your async function instruments.data() called, it'll await return Promise.
just append await at return for your expected result.
var instruments = {
data: async () => {
return await new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
or remove async. it's same as above.
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
I want to make this code work
$('start') // push initial value to an array
.addItem(2) // add another value
.printAll() // print everything in array
.delay(2000) // wait x seconds
.addItem(5)
.printAll()
.start(); // execute all the commands before this line
I created a class with data array to hold the items. and steps array to hold the operations. Then i used chainable promises to execute them. is that the best way to do what i'm trying to achieve? Do I really need to store the operations in an array?
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = (v)=>this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v=>console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let start = Promise.resolve();
this.steps.forEach(({fun, args})=>{
start = start.then(()=>{
return new Promise(resolve=>{
if (fun === window.setTimeout) {
setTimeout(resolve, ...args);
} else {
fun.apply(this, args);
resolve();
}
}
);
}
);
}
);
return this;
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
Although your question belongs to https://codereview.stackexchange.com/ according to me, I tried to think of another implementation without Promises. It works with closures and callbacks only:
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = v => this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v => console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let finalFunction;
this.steps.reverse().forEach(({ fun, args }) => {
if (fun === window.setTimeout) {
finalFunction = finalFunction ? encloseFunctionWithArgs(null, null, finalFunction, next => setTimeout(next, ...args)) : null;
} else {
finalFunction = encloseFunctionWithArgs(fun, args, finalFunction);
}
});
finalFunction();
return this;
}
}
function encloseFunctionWithArgs(fun, args, next, trigger) {
return function () {
if (fun)
fun(args);
if (next)
trigger ? trigger(next) : next();
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
EDIT
I finally managed to achieve what you want:
const base = {
init(startValue) {
this.promise = new Promise(resolve => this.start = resolve).then(() => [startValue]);
return this;
},
addItem(value) {
this.promise = this.promise.then(v => [...v, value]);
return this;
},
printAll() {
this.promise.then(v => v.join()).then(console.log);
return this;
},
delay(value) {
this.promise = this.promise.then(v => new Promise(resolve => setTimeout(() => resolve(v), value)));
return this;
}
}
const factory = base => arg => Object.create(base).init(arg);
const $ = factory(base);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
I'm trying to use the results of a callback function as properties of an object. Here is how I am trying to use the module I am building:
var Screenshot = require("./Screenshot.js")
const test = async function() {
let screenshot = new Screenshot("./screenshots/Screenshot_20180806093446.jpg")
await screenshot.readScreenshot()
console.log(screenshot.text)
}
test() // logs nothing to console
readScreenshot() doesn't seem to be doing its job. I'm sure there's a finer detail I am missing to making this work.
./Screenshot.js code below:
var tesseract = require("node-tesseract");
module.exports = class Screenshot {
constructor(path) {
this.path = path;
this.readScreenshot = this.readScreenshot.bind(this);
}
readScreenshot() {
tesseract.process(this.path, (err, text) => {
if (err) {
throw err;
} else {
this.text = text.split("\n").filter(el => el.trim() !== "");
}
});
}
};
It would also be nice if I can get the constructor of class Screenshot to properly call this function so I do not have to do it manually in app.js
You will have to change the code so that readScreenshot method allows you to return a promise object.
readScreenshot() {
return new Promise((resolve, reject) => {
tesseract.process(this.path, (err, text) => {
if (err) {
reject(err);
} else {
resolve(text.split("\n").filter(el => el.trim() !== ""))
}
});
});
}
//
var Screenshot = require("./Screenshot.js")
const test = async function() {
let screenshot = new Screenshot("./screenshots/Screenshot_20180806093446.jpg")
let resp = await screenshot.readScreenshot()
console.log(resp)
}
test() // logs nothing to console
I know this is a topic where there is a lot of info, but I still can't solve this.
I am working with NodeJs that connects to a mysql server, I have an array, I need to iterate the array and do some stuff with the data, the thing is I need to do a query to the mysql server but I need to the for loop wait until I got the results of the query, I have tried a lot of things, now I am trying with .each method of bluebird but still is not working properly, this is my code.
the initial function is Notification.getByUser
thanks in advance
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
if (result.length === 0) {
resolve(false);
} else {
resolve(true);
}
});
});
};
Notifications.isMatching = function(item, user, type) {
return new Promise((resolve, reject) => {
console.log(type, item, user);
if (item !== null) {
if (item !== user) {
resolve(false);
}
}
resolve(true);
});
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState, cb) {
var where = { status: 1 };
var plainText = '';
var items = Notifications.find({ where });
Promise.each(items, item => {
return Notifications.isMatching(item.isZolver, isZolver, 'isZolver')
.then(() => {
Notifications.isMatching(item.cityId, cityId, 'cityId');
})
.then(() => {
if(item.extraCondition !== null && item.extraCondition !== '') {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// console.log(sql);
Notifications.execute(sql, item.id)
.then(render => console.log('extraCondition', render));
} else {
console.log('extraCondition', true);
}
});
}).then(res => {
// console.log('res del loop', res);
});
cb(null, 'ok');
};
};
There are several issues with your code:
In order to chain promises, you must make sure to return promises that you create within a then callback
You should not need to create a promise for results that are immediately available (isMatching)
The then callback is always executed when a promise fulfils. It does not matter whether you do resolve(false): even if the promised value is false this will still make the promise fulfilled and trigger the then callback(s).
There are some unknowns in your code, like Notifications.find, and what kind of SQL you are executing: does it return a result set, if so, would you not be interested to get that result instead of just resolving to a boolean?
Anyway, here are some applied corrections:
'use strict';
var Promise = require("bluebird");
module.exports = function(Notifications) {
Notifications.execute = function(sql, itemId) {
return new Promise((resolve, reject) => {
this.dataSource.connector.execute(sql, (e, result) => {
console.log('-----RESULT----', itemId);
console.log(result);
console.log('-----ERROR-----', itemId);
console.log(e);
resolve (result.length !== 0); // The `if ... else` is overkill
});
});
};
//You don't need isMatching to return a promise
Notifications.isMatching = function(a, b) {
return a === null || a === b;
};
Notifications.getByUser = function(userId, isZolver, countryId, cityId, userState) {
var where = { status: 1 };
var items = Notifications.find({ where })
// use filter() to limit the items to those you want to execute the SQL for
.filter(item => {
return Notifications.isMatching(item.isZolver, isZolver)
&& Notifications.isMatching(item.cityId, cityId)
&& item.extraCondition !== ''
});
// Return the promise! (don't go back to the old callback system!)
return Promise.each(items, item => {
var sql = item.extraCondition.replace(/:user_id/g, userId);
// Return the promise!
return Notifications.execute(sql, item.id);
}).then(res => {
console.log('res del loop', res);
});
};
};
Note that the signature of Notifications.getByUser does not have the final callback parameter: you should keep using promises once you start with them, so when you call this function, call the result's then method like you would do for any promise:
Notifications.getByUser( .......arguments....).then( function () {
// do something...
});