var is no more!constBut what does that mean?
const is for constant: the pointer is immutable. let is for changing or not yet defined typesvar is not!ES5:
if (true) {
var v = 'v';
}
console.log(v);
ES6:
if (true) {
let l = 'l';
}
console.log(l); // reference error
const and not let?const myModule = require('module');
myModule = 'string'; // error
let basically only for flags or variables that are not defined at first:let connected = false;
let connection;
makeConnection((conn) => {
connected = true;
connection = conn;
});
var to const, and check where it fails. Put let there.Mainly syntactic sugar for anonymous functions:
function(parameter) {
return something;
}
// becomes:
(parameter) => {
return something
}
// or
(parameter) => something
// or
parameter => something
{ } and return can be omitted{ }, the ( ) can be omitted, toothis (great if you use this) – no bind(this) anymoredoSomethingAsync(result => callAnotherFunctionWith(result, andAnotherValue));
()pairs = evens.map(v => ({ even: v, odd: v + 1 }))
(x, y) => x + y
function f(x, y = 7, z = 42) {
return x + y + z;
}
If the exact number of parameters is unknown, we can define an array containing them all:
function f(x, ...a) {
console.log(x);
console.log(a);
}
f(1, 2, 3, 4)
Logs:
1
[2, 3, 4]
--harmony flagconst array = [error, result];
callbackFunction(...array); // callbackFunction is called with (error, result)
--harmony flag// Old:
var string = 'text ' + aVariable + ' more text\n'
+ 'in a new line';
// New:
const string = `text ${aVariable} more text
in a new line`;
`the result is: ${val1 + val2 - func(val3)}!`
ES5:
var something = '';
function f() { ... }
var obj = {
something: something,
f: f,
inlineFunction: function() { ... }
};
obj[someOtherVar] = 3;
ES6:
const something = '';
const f = () => {};
const obj = {
something,
f,
inlineFunction() { ... },
[someOtherVar]: 3
};
array
.filter(key => !!key)
.map(key => key + 1)
.forEach(key => doSomething(key));
Object.keys(object)
.forEach(key => doSomething(object[key]));
import and export will replace require (not in Node.js yet)Map, Set, WeakMap and WeakSet_.each(array, fn), _.map(array, fn), _.reduce(array, fn)array.forEach(fn), array.map(fn), array.reduce(fn)_.every(array, fn), _.some(array, fn), _.find(array, fn)array.every(fn), array.some(fn), array.find(fn)_.includes(array, element)array.includes(element) (Node.js ≥ 6.0)_.compact)array.filter(x => !!x)__uniq)[...new Set(array)]_.findIndex)array.findIndex(fn) Note: these are all array functions. You may need to convert objects to arrays first using Object.keys(obj)
_.assign)Object.assign({}, obj1, { some: thing }) OR{ ...obj1, { some: thing }}{ ...object }datamanager.emailAvailable($scope.registerData.email).then(function (available) {
if (available) {
datamanager.getAuthLink('signup', {clientID: ecAuth.clientID}).then(function (url) {
$http.post(url, {
email: $scope.registerData.email,
password: $scope.registerData.password
}).then(function (registerRes) {
EcAuthService.login(registerRes.data.token).then(function (user) {
ecPublicUser.getUser().then(
function (account) {
if (account) { //sometimes account is not defined
if (account.accountID) {
//do stuff with user
} else {
datamanager.logout();
$location.path('/login');
}
} else {
datamanager.logout();
$location.path('/login');
}
}).catch(console.error.bind(console));
}).catch(console.error.bind(console));
});
}).catch(console.error.bind(console));
} else {
$ionicPopup.alert({
title: 'Fehler',
template: 'Die von dir gewählte Email-Adresse ist bereits in Benutzung'
});
}
}).catch(console.error.bind(console));
Promiseasync.waterfall in cool)return another Promisereturn undefined or a synchronous valuethrow an errorcatch, at the end of the outermost Promise Chainthen for error handlingHappens if you just use Promises like callbacks:
doSomethingAsync(withParameter).then((function (result) {
doSomethingElse(result).then(function (innerResult) {
doThatNow(innerResult).then((finalResult) {
callback(null, finalResult);
}).catch(function(error) {
callback(error);
});
}).catch(function(error) {
callback(error);
})
}).catch(function(error) {
callback(error);
});
(bad!)
Ok, lets put in some structure:
doSomethingAsync(withParameter)
.then((result) => {
doSomethingElse(result)
})
.then((innerResult) => {
doThatNow(innerResult);
})
.then(finalResult => callback(null, finalResult))
.catch(callback)
(still bad! Why?)
The Promises are not returned! Best solution:
doSomethingAsync(withParameter)
.then(doSomethingElse)
.then(doThatNow)
.then(finalResult => callback(null, finalResult))
.catch(callback)
(good!)
getList(ofThings)
.then((result) => {
return result.rows.forEach((row) => {
return db.remove(row.doc);
});
})
.then(function () {
// All deleted! Or not...?
})
.catch(errorHandler);
(bad!)
forEach returns undefined. The second then is called immediately. Errors are not handled.
Better:
getList(ofThings)
.then((result) => {
return Promise.all(result.rows.map((row) => {
return db.remove(row.doc);
});
})
.then(function (weHaveTheResultsHere) {
// All deleted!
})
.catch(errorHandler);
(good! But can be shortened)
forEach returns undefined. The second then is called immediately. Errors are not handled.
Better:
getList(ofThings)
.then(result => Promise.all(
result.rows.map(row => db.remove(row.doc))
))
.then(function (weHaveTheResultsHere) {
// All deleted!
})
.catch(errorHandler);
(good!)
In the old days, Promises were created like that:
function wrapACallbackFunction(name) {
var deferred = q.defer();
callingACallbackFunction(function(error, value) {
if (error) {
deferred.reject(new Error('ohoh'));
} else {
deferred.resolve('Greeting ' + name + ' is not allowed.');
}
});
return deferred.promise;
}
(bad!)
Still often used:
function shouldReturnAPromise(name) {
var deferred = q.defer();
doSomethingThatAlsoReturnsAPromise(name).then(function (result) {
result.addProperty = 'my very own property';
deferred.resolve(result);
}
return deferred.promise;
}
(bad!)
function wrapACallbackFunction(name) {
return new Promise((resolve, reject) => {
callingACallbackFunction((error, value) => {
if (error) {
return reject(error);
}
return resolve(value);
})
});
}
// example 3b:
function shouldReturnAPromise(name) {
return doSomethingThatAlsoReturnsAPromise(name)
.then((result) => {
result.addProperty = 'my very own property';
return result;
})
}
(good!)
function doSomethingWithJSONString(string) {
const json = JSON.parse(string);
json.myOwnProp = anotherObject.key;
return new Promise((resolve, reject) => {
resolve(json)
})
.then(callSomething)
.catch(handleError);
}
(bad! Why?)
What if the string is no JSON? JSON.parse will throw.
What if anotherObject has no Property key? It will throw.
function doSomethingWithJSONString(string) {
return Promise.resolve(string)
.then(JSON.parse)
.then((json) => {
json.myOwnProp = anotherObject.key;
return json;
})
.then(callSomething)
.catch(handleError);
}
(good!)
Promise.resolve;Promise.reject(error), but its the same as throw error inside a Promise chain.then(success, error)somePromise()
.then(() => someOtherPromise())
.catch((error) => {
// handle error
});
somePromise()
.then(
() => someOtherPromise(),
(error) => {
// handle error
}
);
Is that the same?
catch, never the second argument to thenIf we want to execute multiple Promises in Parallel, we have Promise.all(promiseArray). What if we want them executed in sequence?
function executeSequentially(promises) {
var result = Promise.resolve();
promises.forEach((promise) => {
result = result.then(promise);
});
return result;
}
const files = ['file1', 'file2', 'file3'];
return executeSequentially(files.map(deleteFile))
.catch(errorHandler);
(bad! why?)
It is still parallel, because the Promises execute immediately when they are created. Also, executeSequentially returns instantly without waiting for anything.
function executeSequentially(promises) {
var result = Promise.resolve();
promises.forEach((promise) => {
result = result.then(promise);
});
return result;
}
const files = ['file1', 'file2', 'file3'];
return executeSequentially(files.map(file => () => deleteFile(file))
.catch(errorHandler);
(good!)
The executeSequentially can be further tuned using reduce
function executeSequentially(promises) {
return promises.reduce((result, promise) => result.then(promise), Promise.resolve());
}
const files = ['file1', 'file2', 'file3'];
return executeSequentially(files.map(file => () => deleteFile(file))
.catch(errorHandler);
// OR:
return files
.map(file => () => deleteFile(file))
.reduce((r, p) => r.then(p), Promise.resolve())
.catch(errorHandler);
getUserByName('nolan')
.then(user => getUserAccountById(user.id))
.then((userAccount) => {
return { user, userAccount };
})
.catch(errorHandler);
Throws an error.
let user;
getUserByName('nolan')
.then((result) => {
user = result;
return getUserAccountById(user.id)
})
.then((userAccount) => {
return { user, userAccount };
})
.catch(errorHandler);
Solution 1 (★★★☆☆)
getUserByName('nolan')
.then((result) => {
return getUserAccountById(user.id)
.then((userAccount) => {
return { user, userAccount };
})
})
.catch(errorHandler);
Solution 2 (★★★★☆)
function addAccountToUserObject(user) {
return getUserAccountById(user.id)
.then((userAccount) => {
return { user, userAccount };
})
}
getUserByName('nolan')
.then(addAccountToUserObject)
.catch(errorHandler);
Solution 3 (★★★★★)
datamanager.emailAvailable($scope.registerData.email).then(function (available) {
if (available) {
datamanager.getAuthLink('signup', {clientID: ecAuth.clientID}).then(function (url) {
$http.post(url, {
email: $scope.registerData.email,
password: $scope.registerData.password
}).then(function (registerRes) {
EcAuthService.login(registerRes.data.token).then(function (user) {
ecPublicUser.getUser().then(
function (account) {
if (account) { //sometimes account is not defined
if (account.accountID) {
//do stuff with user
} else {
datamanager.logout();
$location.path('/login');
}
} else {
datamanager.logout();
$location.path('/login');
}
}).catch(console.error.bind(console));
}).catch(console.error.bind(console));
});
}).catch(console.error.bind(console));
} else {
$ionicPopup.alert({
title: 'Fehler',
template: 'Die von dir gewählte Email-Adresse ist bereits in Benutzung'
});
}
}).catch(console.error.bind(console));
datamanager.emailAvailable($scope.registerData.email)
.then((available) => {
if (!available) {
return $ionicPopup.alert({
title: 'Fehler',
template: 'Die von dir gewählte Email-Adresse ist bereits in Benutzung'
});
}
return datamanager.getAuthLink('signup', {clientID: ecAuth.clientID})
.then(url => $http.post(url, {
email: $scope.registerData.email,
password: $scope.registerData.password
})
.then(registerRes => EcAuthService.login(registerRes.data.token))
.then(user => ecPublicUser.getUser())
.then((account) => {
if (account && account.accountID) {
//do stuff with user
} else {
datamanager.logout();
$location.path('/login');
}
})
})
.catch(console.error.bind(console));
Good News: IntelliJ/WebStorm knows all about ES6 and helps us using it correctly! (Preferences, Languages & Frameworks, JavaScript, Language Version)
What we need to configure, is a good Style Guide and automatic Linting.
https://github.com/airbnb/javascript/
A mostly reasonable approach to JavaScript
const, avoid var. === and !== over == and !=thisSimple linting, you only need one configuration file in your root projects folder.
More Doc and how to configure IntelliJ / Webstorm: Airbnb Style Guide in IntelliJ IDEA / WebStorm