Cancel promises in Promise.map if one is rejected (Bluebird 3) - bluebird

I use Bluebird 3 with enabled cancellation. Is cancellation the tool to use in the following use case:
var resourcesPromise = Promise.map(resourceIds, function(id) {
return loadResource(id);
});
resourcesPromise.catch(function() {
resourcesPromise.cancel();
});`
If one of the resources fails to load, resourcesPromise will be rejected, and I want to stop the loading of all other resources. But as far as I can tell, cancelling resourcesPromise doesn't work, because it is already rejected.
Edit: I'm currently considering variants of the following:
var resourcesPromise = new Promise(function(resolve, reject) {
var intermediatePromise = Promise.map(resourceIds, function(id) {
return loadResource(id).catch(function(error) {
intermediatePromise.cancel();
reject(error);
});
}).then(resolve, reject);
});
(I may have found a legitimate use for the ".then(resolve, reject)" anti-pattern!)
Any ideas why Promise.map doesn't work like that?

With map you are parallelizing the resource loading, maybe is better to use Promise.each to laoad resources in sequence. In such case you don't need to cancel the promise to stop loading the remaining resources when one fails.
var resourcesPromise = Promise.map(resourceIds, function(id) {
return loadResource(id);
});
resourcesPromise.catch(function() {
resourcesPromise.cancel();
});`
Another option would be to pass to the map function an option object like in which you can specify the concurrency limit.
Promise.resolve(resourceIds).
map(function(id) {
return loadResource(id);
}, {concurrency: n}).
catch(function(e) {
//do some error handling
});

Related

How do I use promises in a Chrome extension?

What I am trying to do is create a chrome extension that creates new, nested, bookmark folders, using promises.
The function to do this is chrome.bookmarks.create(). However I cannot just
loop this function, because chrome.bookmarks.create is asynchronous. I need to wait until the folder is created, and get its new ID, before going on to its children.
Promises seem to be the way to go. Unfortunately I cannot find a minimal working example using an asynchronous call with its own callback like chrome.bookmarks.create.
I have read some tutorials 1, 2, 3, 4. I have searched stackOverflow but all the questions do not seem to be about plain vanilla promises with the chrome extension library.
I do not want to use a plugin or library: no node.js or jquery or Q or whatever.
I have tried following the examples in the tutorials but many things do not make sense. For example, the tutorial states:
The promise constructor takes one argument—a callback with two
parameters: resolve and reject.
But then I see examples like this:
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
How this works is a mystery to me.
Also, how can you call resolve() when its never been defined? No example in the tutorials seem to match real life code. Another example is:
function isUserTooYoung(id) {
return openDatabase() // returns a promise
.then(function(col) {return find(col, {'id': id});})
How do I pass in col, or get any results!
So if anyone can give me a minimal working example of promises with an asynchronous function with its own callback, it would be greatly appreciated.
SO wants code, so here is my non-working attempt:
//loop through all
function createBookmarks(nodes, parentid){
var jlen = nodes.length;
var i;
var node;
for(var i = 0; i < nodes.length; i++){
var node = nodes[i];
createBookmark(node, parentid);
}
}
//singular create
function createBookmark(node, parentid){
var bookmark = {
parentId : parentid,
index : node['index'],
title : node['title'],
url : node['url']
}
var callback = function(result){
console.log("creation callback happened.");
return result.id; //pass ID to the callback, too
}
var promise = new Promise(function(resolve, reject) {
var newid = chrome.bookmarks.create(bookmark, callback)
if (newid){
console.log("Creating children with new id: " + newid);
resolve( createBookmarks(bookmark.children, newid));
}
});
}
//allnodes already exists
createBookmarks(allnodes[0],"0");
Just doesn't work. The result from the callback is always undefined, which it should be, and I do not see how a promise object changes anything. I am equally mystified when I try to use promise.then().
var newid = promise.then( //wait for a response?
function(result){
return chrome.bookmarks.create(bookmark, callback);
}
).catch(function(error){
console.log("error " + error);
});
if (node.children) createBookmarks(node.children, newid);
Again, newid is always undefined, because of course bookmarks.create() is asynchronous.
Thank you for any help you can offer.
Honestly, you should just use the web extension polyfill. Manually promisifying the chrome APIs is a waste of time and error prone.
If you're absolutely insistent, this is an example of how you'd promisify chrome.bookmarks.create. For other chrome.* APIs, you also have to reject the callback's error argument.
function createBookmark(bookmark) {
return new Promise(function(resolve, reject) {
try {
chrome.bookmarks.create(bookmark, function (result) {
if (chrome.runtime.lastError) reject(chrome.runtime.lastError)
else resolve(result)
})
} catch (error) {
reject(error)
}
})
}
createBookmark({})
.then(function (result) {
console.log(result)
}).catch(function (error) {
console.log(error)
})
To create multiple bookmarks, you could then:
function createBookmarks(bookmarks) {
return Promise.all(
bookmarks.map(function (bookmark) {
return createBookmark(bookmark)
})
)
}
createBookmarks([{}, {}, {}, {}])
.catch(function (error) {
console.log(error)
})
Take the advantage of the convention that the callback function always be the last argument, I use a simple helper function to promisify the chrome API:
function toPromise(api) {
return (...args) => {
return new Promise((resolve) => {
api(...args, resolve);
});
};
}
and use it like:
toPromise(chrome.bookmarks.create)(bookmark).then(...);
In my use case, it just works most of the time.

synchronous mysql queries in nodejs

I'm pretty new to nodejs and I'm having some difficulties to understand how to use the mysql connection object.
My problem is not in the code but in the design pattern.
lets say I have a user module
module.exports = function(){
return{
id: "",
load: function(id){
var sql = 'SELECT * from users where id = '+ DB.escape(id);
console.log(1);
DB.query(sql, function (err, rows) {
this.id = rows[0].id; // not working
console.log(rows[0].id); // prints the id 4
console.log(2);
});
console.log(3);
}
}
}
from outside the module i run the next code
var user = require('../modules/user');
var selected_user = user();
console.log("entering users me route");
selected_user.load(4);
console.log("user id is " + selected_user.id); //This does not print the id 4
when I run the code, the console logs 1, then 3, and then 2.
This is due to the asynchronous flow of node js.
But if I'm building a website, and I need the query to end in order to populate my user object before I send the HTML to the browser???
What's the right way to do it ?
Also when I try to populate the id property of user in the id i receive from the DB it does not work.
Any ideas?
Thanks
There are several ways to do this. I would go with Promises.
Suppose you have an asynchronous function "getUsers".
It looks like this:
function getUsers() {
longQuery(function(err, result){
// What to do with result?
});
You need to rewrite it to be able to use the result.
Let's try:
function getUsers() {
return new Promise(function(resolve, reject) {
longQuery(function(err, result){
if(err) reject(err)
else resolve(result)
});
});
Now this function returns a promise. What do we do with that promise?
function handleRequest(req, res) {
getUsers().then(function(result) {
// Do stuff with result
res.send(myProcessedData);
}).catch(function(err) {console.log(err)};
}
This could also have been done with callbacks, passing the response object as a parameter to the query function, and many other ways, but I think promises are a very elegant way for handling this.
this.id = rows[0].id; // not working
The above line is not working because you are setting it to this.id from inside a callback function. When you are inside a callback function this does not mean the this in the main object.
For more discussion about this: see How to access the correct `this` context inside a callback?
To tackle the asynchronous nature of javascript you can either use promise like the answer from matanso or you can pass a callback function to your load method. So your load: function(id) method will be load: function(id, callbackFunction) and call the callback function when you get all the data that you need.

Lightswitch html how to know when async iteration is done

I have an async operation inside another async operation. I wonder how can i know when everything is done.
Here is my code:
msls.showProgress(msls.promiseOperation(function (operation) {
screen.Staff.getConfirmedWaiters().then(function (result) {
result.each(function (item) {
item.getWaiter().then(function (result) {
if (result.Gender == "Female") {
confirmedGirls++;
}
});
});
operation.complete(confirmedGirls);
});
}).then(function (result) {
First I load the ConfirmedWaiters collection. Once it is completed I iterate every entity and load a child entity async, so I want to know when the iteration is complete!? but the problem is that its returning right away because its async so how can I wait till the iteration is complete and then call operation.complete()?
In order to tackle this type of asynchronous challenge, you need to join your promises.
The following blog posts provide some background on promises and are a useful read for any LightSwitch HTML Client developer (it's always worth bearing in mind that LightSwitch HTML is largely WinJS based and any WinJS promise related material is worth reading): -
Promises in LightSwitch (Justin Anderson)
All about promises (for Windows Store apps written in JavaScript)
Based on the 'Joining parallel promises' approach covered in the second of these blog posts, your code should end up similar to the following which should achieve the desired result (though it may encounter a Gender Equality Exception ;-)
msls.showProgress(msls.promiseOperation(function (operation) {
screen.Staff.getConfirmedWaiters().then(function (result) {
var ps = [];
result.each(function (item) {
var p = item.getWaiter().then(function (result) {
if (result.Gender == "Female") {
confirmedGirls++;
}
});
ps.push(p);
});
WinJS.Promise.join(ps).then(function onComplete(result) {
operation.complete(confirmedGirls);
}, function onError(error) {
operation.error(error);
});
});
}).then(function (result) {
Hopefully this should do the trick. If not, uploading a sample project to provide more background may help.

Service retrieves data from datastore but does not update ui

I have a service which retrieves data from the datastore (Web SQL). Afterwards, it stores the data in a AngularJS array. The problem is that this does not initiate changes to the UI.
Contrary, if after the retrieval of data from datastore, I call a web services using a $get method and append the results to the previous array, all data updates the UI.
Any suggestions? Is it possible that I fill the array before the Angular binds the variable?
Can I somehow delay the execution of the service?
Most of the code has been taken from the following example: http://vojtajina.github.io/WebApp-CodeLab/FinalProject/
In order for the UI to magically update, some changes must happen on properties of the $scope. For example, if retrieving some users from a rest resource, I might do something like this:
app.controller("UserCtrl", function($http) {
$http.get("users").success(function(data) {
$scope.users = data; // update $scope.users IN the callback
}
)
Though there is a better way to retrieve data before a template is loaded (via routes/ng-view):
app.config(function($routeProvider, userFactory) {
$routeProvider
.when("/users", {
templateUrl: "pages/user.html",
controller: "UserCtrl",
resolve: {
// users will be available on UserCtrl (inject it)
users: userFactory.getUsers() // returns a promise which must be resolved before $routeChangeSuccess
}
}
});
app.factory("userFactory", function($http, $q) {
var factory = {};
factory.getUsers = function() {
var delay = $q.defer(); // promise
$http.get("/users").success(function(data){
delay.resolve(data); // return an array of users as resolved object (parsed from JSON)
}).error(function() {
delay.reject("Unable to fetch users");
});
return delay.promise; // route will not succeed unless resolved
return factory;
});
app.controller("UserCtrl", function($http, users) { // resolved users injected
// nothing else needed, just use users it in your template - your good to go!
)
I have implemented both methods and the latter is far desirable for two reasons:
It doesn't load the page until the resource is resolved. This allows you to place a loading icon, etc, by attaching handlers on the $routeChangeStart and $routeChangeSuccess.
Furthermore, it plays better with 'enter' animations in that, all your items don't annoyingly play the enter animation every time the page is loaded (since $scope.users is pre populated as opposed to being updated in a callback once the page has loaded).
Assuming you're assigning the data to the array in the controller, set an $scope.$apply() after to have the UI update.
Ex:
$scope.portfolio = {};
$scope.getPortfolio = function() {
$.ajax({
url: 'http://website.com:1337/portfolio',
type:'GET',
success: function(data, textStatus, jqXHR) {
$scope.portfolio = data;
$scope.$apply();
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(errorThrown);
}
});
};

HTML5 Web Workers in NodeJS?

Anyone knows what the status of Web Worker support in NodeJS is? I found a two year old implementation, node-webworkers, but it didn't run with the current build of NodeJS.
Now there is https://github.com/audreyt/node-webworker-threads which appears to be actively maintained.
Worker Threads reached stable status in 12 LTS. Usage example
const {
Worker, isMainThread, parentPort, workerData
} = require('worker_threads');
if (isMainThread) {
module.exports = function parseJSAsync(script) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, {
workerData: script
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker stopped with exit code ${code}`));
});
});
};
} else {
const { parse } = require('some-js-parsing-library');
const script = workerData;
parentPort.postMessage(parse(script));
}
You can use the child processes, they solve similar problems.
You can look at the specifics of the HTML5 WebWorker source.
With a little care, you can 'redress' the WebWorker to fit as a Node.js worker, by adding a prelude that may look something like this:
const { parentPort } = require('worker_threads')
global.postMessage = function(msg){
parentPort.postMessage(msg)
}
var handler
global.addEventListener = function(kind, callback){
handler = callback
}
parentPort.on('message', msg => {
handler(msg)
})
The specific HTML5 worker added a message event handler using addEventListener, so I registered such a function in global and saved the handler. I also had to supply a postMessage implementation. Finally I registered a Node.js message handler that invokes the HTML5 handler.
Everything works perfectly. No need for any special dependency, just looking at the HTML5 worker code and identify the points where it deals with messages.