Last active
August 24, 2019 16:05
-
-
Save dhval/1646ac665ca78d7e8af3 to your computer and use it in GitHub Desktop.
Using bluebird promises
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
/** | |
Demostrate a practical use case with Bluebird promise api. | |
Read a line delimited file and store into sqlite3 db. | |
Promise.promisifyAll - Converting synchromous api's to Async. | |
Promise.using and Promise.disposer - Resource managment. | |
Promise.map and Promise.spread - Leveraging arrays in promises. | |
Notes: | |
1. You can forget everything but always remember to return something from callbacks ! | |
2. I am prefferring named function expression over anoymous functions as it is easy to | |
ready and meaningful while debugging. | |
see: | |
http://bluebirdjs.com/docs/api | |
http://bluebirdjs.com/docs/api/disposer.html | |
see http://bluebirdjs.com/docs/api/promise.map.html | |
https://github.com/grumdrig/node-sqlite | |
**/ | |
var fs = require('fs'), | |
path = require('path'), | |
Promise = require('bluebird'), | |
// Promisify all functions with async support. | |
sqlite3 = Promise.promisifyAll(require('sqlite3').verbose()), | |
patternUser = new RegExp("\\w*\\.\\w*"), | |
currentTime = new Date().getTime(); | |
function fileRead(dir, file) { | |
// we can do this for kicks, Promise.promisify(fs.readFileSync) | |
return fs.readFileSync(path.join(dir, file), 'utf8', function(err, data) { | |
console.log(err + "err"); | |
return data; | |
}); | |
} | |
// Read a file containing user names, \n delimited. Filter out bad names. | |
var data = fileRead("/Users/dhval", "users.txt").split(/\n/) | |
.filter(function(user) { | |
return patternUser.test(user); | |
}).map(function(user) { | |
return user.replace(/[^a-zA-Z0-9.]/g, ""); | |
}); | |
// Returns a promise of user count with the given user name. | |
function userExists(con, userName) { | |
var promise = con.getAsync("select count(*) as count from user where user = ?", userName).then(function(row) { | |
return row.count; | |
}, function(err) { | |
console.log(err); | |
return -1; | |
}); | |
return promise; | |
} | |
// Returns a promise of new connection. | |
function createConnection(dbFile) { | |
var connection = new sqlite3.Database(dbFile); | |
// Making sure a table exists or create one. By returning a connection we avoid anti pattern of creating promise within promise. | |
return connection.runAsync("CREATE TABLE if not exists user (id INT, user TEXT, pwd TEXT, createDt INT, updateDt INT)").then(function() { | |
return connection; | |
}); | |
} | |
// Attaches a disposer to a promise. | |
function getConnection(dbFile) { | |
return createConnection(dbFile).disposer(function(connection, promise) { | |
if (connection) { | |
console.log("closing db connection, success=" + promise.isFulfilled()); | |
connection.close(); | |
} | |
}); | |
} | |
// You can see from logs that everythinng happen in proper sequence. Still it has a race condition because of dirty reads. | |
Promise.using(getConnection('users.db'), function(connection) { | |
// Promise.dispose will be fired whenever we 'complete()' this promise. | |
var promise = new Promise(function(complete, reject) { | |
var stmt = connection['stmt'] = connection.prepare("INSERT INTO user(user, createDt) VALUES (?,?)"); | |
console.log("count#" + data.length); | |
Promise.map(data, function(userId) { | |
return connection.getAsync("select count(*) as count from user where user = ?", userId).then(function(row) { | |
console.log("got results: " + userId + "#" + row.count); | |
return row.count ? row.count : 0; | |
}).then(function(count) { | |
if (count == 0) { | |
console.log("insert, " + userId); | |
return stmt.runAsync(userId, currentTime);; | |
} else { | |
// it is a good habbit to return something from callbacks. | |
return true; | |
} | |
}); | |
}).spread(function() { | |
console.log("complete, calling disposers"); | |
stmt.finalize(); | |
complete(); | |
}).catch(function(e) { | |
console.log(e); | |
}) | |
}); | |
return promise; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "js-dev", | |
"version": "1.2.0", | |
"description": "cmd line JS scripts.", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "", | |
"license": "ISC", | |
"dependencies": { | |
"bluebird": "^3.0.0", | |
"sqlite3": "^3.1.0" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<title>Hello World</title> | |
<script src="https://unpkg.com/react@15/dist/react.js"></script> | |
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script> | |
</head> | |
<body> | |
<div id="root"></div> | |
<script> | |
window.onload = function() | |
{ | |
class Greetings extends React.Component | |
{ | |
render() | |
{ | |
return React.createElement('h1', null, 'Greetings, ' + this.props.name + '!'); | |
} | |
} | |
ReactDOM.render( | |
React.createElement(Greetings, { name : 'User' }), | |
document.getElementById('root') | |
); | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment