Monday, January 22, 2018

Converting an object to a string

I would recommend using JSON.stringify, which converts the set of the variables in the object to a JSON string. Most modern browsers support this method natively, but for those that don't, you can include a JS version:
var obj = {
  name: 'myObj'
};

JSON.stringify(obj);

Tuesday, January 16, 2018

SERIES JAVASCRIPT SIDA – SỰ BÁ ĐẠO CỦA ASYNC/AWAIT TRONG JS

Lâu rồi chưa viết bài về kĩ thuật nên hôm nay viết một bài để dân tình biết là mình vẫn còn code nhé!
Kì này, chúng ta sẽ tìm hiểu về async/await, một cặp từ khoá rất bá đạo trong JavaScript chuẩn ES2017. Biết cách dùng async/await sẽ giúp ta viết code ngắn gọn, hiệu quả và dễ dàng hơn nhiều nhé.

Nhắc lại kiến thức

JavaScript là ngôn ngữ single-thread, tức là chỉ có một thread duy nhất để thực thi các dòng lệnh. Nếu chạy theo cơ chế đồng bộ (synchonous) thì khi thực hiện tính toán phức tạp, gọi AJAX request tới server, gọi database (trong NodeJS), thread này sẽ dừng để chờ, làm toàn bộ trình duyệt bị… treo.
Để tránh điều này, hầu hết code gọi AJAX request hoặc database trong JavaScript đều chạy theo cơ chế bất đồng bộ (asynchnous). Ban đầu, việc chạy code asynchnous trong JavaScript được hiện thực nhờ callback (như đoạn code bên dưới).

// Truyền callback vào hàm ajax

var callback = function(image){

console.log(image);

};

ajax.get("gaixinh.info", callback);


// Có thể viết gọn như sau

ajax.get("gaixinh.info", function(image) {

console.log(image);

})
view rawajax_callback.js hosted with ❤ by GitHub
Tất nhiên, vì callback có một số nhược điểm như code dài dòng, callback hell,… nên người ta tạo ra 1 pattern mới gọi là Promise!
Các bạn nên xem lại kiến thức về Callback trong JavaScript  Promise trong JavaScript để có thể hiểu kiến thức phía dưới bài này nhé!

Từ callback, promise đến Async/Await

Promise đã giải quyết khá tốt những vấn đề của callback. Code trở nên dễ đọc, tách biệt và dễ bắt lỗi hơn.
Code trở nên gọn đẹp khi chuyển từ callback qua promise.
Tuy nhiên, dùng promise đôi khi ta vẫn thấy hơi khó chịu vì phải truyền callback vào hàm then và catch. Code cũng sẽ hơi dư thừa và khó debug, vì toàn bộ các hàm then chỉ được tính là 1 câu lệnh nên không debug riêng từng dòng được.
May thay, trong ES7 một phép màu mang tên async/await đã ra đời. (Mình nghi 99% là phép màu này ăn cắp từ C# hay ho, vì C# đã có async/await từ thời ông địa cởi trường rồi cơ).
Xem object Promise như thể chúng là đối tượng đồng bộ
Vậy async/await có gì hay ho? Chúng giúp chúng ta viết code trông có vẻ đồng bộ (synchonous), nhưng thật ra lại chạy bất đồng bộ (asynchonous). 
Như trong hình phía trên, hàm findRandomImgPromise là hàm bất đồng bộ, trả về một Promise. Với từ khoá await, ta có thể coi hàm này là đồng bộ, câu lệnh phía sau chỉ được chạy sau khi hàm này trả về kết quả.
Bấm Run Pen để xem demo nhé!

Tại sao nên dùng async/await?

Như mình đã nói, async/await có một số ưu điểm vượt trội so với promise:
  • Code dễ đọc hơn rất rất nhiều, không cần phải then rồi catchgì cả, chỉ viết như code chạy tuần tự, sau đó dùng try/catch thông thường để bắt lỗi.
  • Viết vòng lặp qua từng phần tử trở nên vô cùng đơn giản, chỉ việc await trong mỗi vòng lặp.
  • Debug dễ hơn nhiều, vì mỗi lần dùng await được tính là một dòng code, do đó ta có thể đặt debugger để debug từng dòng như thường.
  • Khi có lỗi, exception sẽ chỉ ra lỗi ở dòng số mấy chứ không chung chung là un-resolved promise.
  • Với promise hoặc callback, việc kết hợp if/else hoặc retry với code asynchnous là một cực hình vì ta phải viết code lòng vòng, rắc rối. Với async/await, việc này vô cùng dễ dàng.
Async/Await làm code trở nên gọn gàng sạch đẹp như thế nào!
Một demo loop khá cool bằng async await

Bất cập của async/await

Tất nhiên, async/await cũng có một số bất cập mà các bạn cần phải lưu ý khi sử dụng:
  • Không chạy được trên các trình duyệt cũ. Nếu dự án yêu cầu phải chạy trên các trình duyệt cũ, bạn sẽ phải dùng Babel để transpiler code ra ES5 để chạy.
  • Khi ta await một promise bị reject, JavaScript sẽ throw một Exception. Do đó, nếu dùng async await mà quên try catch thì lâu lâu chúng ta sẽ bị… nuốt lỗi hoặc code ngừng chạy.
  • async và await bắt buộc phải đi kèm với nhau! await chỉ dùng được trong hàm async, nếu không sẽ bị syntax error. Do đó, async/await sẽ lan dần ra toàn bộ các hàm trong code của bạn.
async/await vẫn chưa chạy được trên IE, Microsoft Edge 14 và một số trình duyệt cũ hơn

Áp dụng async/await vào code

Về bản chất, một hàm async sẽ trả ra một promise, tương ứng với Task trong C#. Do vậy, để có thể dùng async/await một cách hiệu quả, các bạn phải nắm rõ cơ chế làm việc của Promise nhé!
Hiện tại các phiên bản mới nhất của Chrome, Edge và Firefox đã hỗ trợ async/await, nếu dự án không bắt hỗ trợ các trình duyệt cũ thì các bạn cứ thoải mái dùng async/await để code gọn đẹp hơn nhé.
Ngoài ra, nếu các bạn sử dụng NodeJS, có thể sử dụng combo Promisify + Async/Await như sau:
  1. Sử dụng Bluebird hoặc util.promisify (Node 8 trở lên) để biến các hàm callback của NodeJS thành Promise.
  2. Dùng async/await để lấy kết quả từ các Promise này.

Kết

Bài viết kì này khá phức tạp, lại cần nhiều kiến thức nền về JavaScript nên sẽ hơi khó hiểu.
Các bạn ráng đọc lại 2,3 lần, xem lại các code mẫu để hiểu nha. Nếu có thắc mắc hay có gì cần chia sẻ, các bạn cứ thoải mái comment nhé!

SERIES JAVASCRIPT SIDA – PROMISE – HỨA THẬT NHIỀU THẤT HỨA THẬT NHIỀU

Bài viết này sẽ giới thiệu về khái niệm promise và các ứng dụng của nó trong javascript. Promise được sử dụng khá nhiều ở cả front-end (AngularJS) và back-end(NodeJS), do đó nắm vững khái niệm này sẽ giúp bạn rất nhiều trong việc code và … trả lời phỏng vấn.
Lưu ý: Bài viết sử dụng nhiều ngôn ngữ 16+, khuyến phụ nữ dưới 18 tuổi và trẻ em có thai không nên đọc.

Lập trình bất đồng bộ trong Javascript

Bạn nào từng làm AJAX đều biết rằng Javascript kết nối với server theo cơ chế bất đồng bộ, các hàm phía sau không đợi hàm AJAX chạy xong mà tiếp tục chạy luôn.

// Hàm ajax chạy bất đồng bộ, do đó giá trị xxxImage là undefined

var xxxImage = ajax.get("gaixinh.info");

console.log(xxxImage);

view rawajax.js hosted with ❤ by GitHub
Do đó, để lấy kết quả của hàm ajax, ta phải truyền cho nó 1 callback. Sau khi hàm AJAX lấy được kết quả, nó sẽ gọi hàm callback với kết quả thu được.

// Truyền callback vào hàm ajax

var callback = function(image){

console.log(image);

};

ajax.get("gaixinh.info", callback);


// Có thể viết gọn như sau

ajax.get("gaixinh.info", function(image) {

console.log(image);

})
view rawajax_callback.js hosted with ❤ by GitHub
Cách làm này có gì không ổn? Sử dụng callback chồng chéo sẽ làm code trở nên rối rắm, khó đọc; việc bắt exception, hiển thị lỗi trở cũng nên phức tạp.

// Do javascript bất đồng bộ, bạn không thể lấy dữ liệu lần lượt kiểu này

var xe = xin_mẹ_mua_xe(); // Chờ cả năm mới có xe

var gái = chở_gái_đi_chơi(xe); // Lấy xe chở gái đi chơi

var abcd = chở_gái_vào_hotel(y); // Đi chơi xong chở gái đi đâu đó


// Mà phải sử dụng đống callback "gớm ghiếc", tạo thành callback hell

xin_mẹ_mua_xe(function(xe) {

chở_gái_đi_chơi(xe, function(gái) {

chở_gái_vào_hotel(hotel, function(z) {

// Làm gì đó, ai biết

});

});

});
view rawcallback_hell.js hosted with ❤ by GitHub
Để giải quyết vấn đề này, các bác developer đã sáng tạo ra một khái niệm gọi là promise.

Promise là chi? Promise là… lời hứa!

Khái niệm promise được MDN giải thích một cách khá mù mờ và hơi… khó hiểu:
The Promise object is used for asynchronous computations. A Promise represents an operation that hasn't completed yet, but is expected in the future.
JSPromise
Để dễ hiểu, mình gọi Promise là lời hứa. Tương tự như trong thực tế, có người hứa rồi làm, có người hứa rồi … thất hứa.
Một lời hứa có 3 trạng thái sau:
  • pending: Hiện lời hứa chỉ là lời hứa suông, còn đang chờ người khác thực hiện
  • fulfilled: Lời hứa đã được thực hiện
  • reject: Bạn đã bị thất hứa, hay còn gọi là bị “xù”
Khi xưa, để dụ bạn cố gắng học hành, bố mẹ bảo “Ráng đậu đại học bố mẹ sẽ mua cho con BMW đi học cho bằng bạn bằng bè”. Lúc này, thứ bạn nhận được là một lời hứa, chứ không phải xe BMW.

// Hàm này trả ra lời hứa chứ không phải chiếc BMW

function hứa_cho_có() {

return Promise((thuc_hien_loi_hua, that_hua) => {

// Sau 1 thời gian dài dài dàiiiiiii

// Nếu vui bố mẹ sẽ thực hiện lời hứa

if (vui) {

thuc_hien_loi_hua("Xe BMW");

// Lúc này trạng thái của lời hứa là fulfilled

} else {

that_hua("Xe dap");

// Lúc này trạng thái của lời hứa là rejected

}

});

}


// Lời hứa bây giờ đang là pendding

// Nếu được thực hiện, bạn có "Xe BMW"

// Nếu bị reject, bạn có "Xe đạp"

var promise = hứa_cho_có();

promise

.then((xe_bmw) => {

console.log("Được chiếc BMW vui quá");

})

.catch((xe_dap) => {

console.log("Được chiếc xe đạp ....");

});
view rawbmw_promise.js hosted with ❤ by GitHub
Khi lời hứa được thực hiện, promise sẽ gọi callback trong hàm then. Ngược lại, khi bị thất hứa, promise sẽ gọi callback trong hàm catch.
Quay lại với ví dụ ở đầu bài. Lúc này hàm ajax sẽ trả ra một lời hứa. Hàm này hứa là “Nếu lấy ảnh XXX, ta sẽ đưa cho mày”. Lúc này kết quả của hàm là một promise, chứ không phải là ảnh XXX.

function get(url) {

return new Promise((resolve, reject) => {

// Lấy hình từ gái xinh.com

// Nếu bị lỗi thì đành thất hứa

if (error) reject("Error");


// Nếu lấy được thì thực hiện lời hứa

resolve(xxxImage);

});

}


var promise = ajax.get("gaixinh.info");

// Có hình thì fap, không có thì alert lỗi

promise

.then(image => fap)

.catch((error) => alert(error));
view rawajax_promise.js hosted with ❤ by GitHub

Ủa, cũng là dùng callback thôi mà??

Ừ, hồi mới tìm hiểu về promise mình cũng nghĩ tương tự như các bạn vậy đó. Vậy thì promise có gì hay mà phải dùng?
Promise hay ở các điểm sau:
  1. Promise hỗ trợ “chaining”
  2. Promise giúp bắt lỗi dễ dàng hơn
  3. Xử lý bất đồng bộ
Cùng phân tích các điểm hay của Promise nhé.
1. Promise chaining
Giá trị trả về của hàm then là 1 promise khác. Do vậy ta có thể dùng promise gọi liên tiếp các hàm bất đồng bộ. Có thể viết lại hàm phía trên như sau:

// Dùng callback hell

xin_mẹ_mua_xe(function(xe) {

chở_gái_đi_chơi(xe, function(gái) {

chở_gái_vào_hotel(hotel, function(z) {

// Làm gì đó, ai biết

});

});

});


// Dùng promise, code gọn nhẹ dễ đọc

xin_mẹ_mua_xe

.then(chở_gái_đi_chơi)

.then(chở_gái_vào_hotel)

.then(function() { /*Làm gì đó, ai biết*/ });
2. Bắt lỗi với promise
Trong ví dụ trên, ta gọi lần lượt 3 hàm: xin_mẹ_mua_xechở_gái_đi_chơichở_gái_vào_hotel.
Chỉ cần một trong 3 hàm này bị lỗi, promise sẽ chuyển qua trạng thái reject. Lúc này callback trong hàm catch sẽ được gọi. Việc bắt lỗi trở nên dễ dàng rất nhiều

// Khi một function bị lỗi, promise bị reject (thất hứa)

function chở_gái_vào_hotel() {

return Promise((response, reject) => {

reject("Xin lỗi hôm nay em đèn đỏ");

});

}



xin_mẹ_mua_xe

.then(chở_gái_đi_chơi)

.then(chở_gái_vào_hotel)

.then(function() { /*Làm gì đó, ai biết*/ })

.catch(function(err) {

console.log(err); //"Xin lỗi hôm nay em đèn đỏ"

console.log("xui vl");

});

view rawpromise_catch.js hosted with ❤ by GitHub
3. Xử lý bất đồng bộ
Giả sử bạn muốn 3 hàm AJAX khác nhau. Bạn cần kết quả trả về của 3 hàm này trước khi tiếp tục chạy. Với callback, việc này sẽ khá khó thực hiện. Tuy nhiên, promise hỗ trợ hàm Promise.all, cho phép gộp kết quả của nhiều promise lại với nhau.

// Ba hàm này phải được thực hiện "cùng lúc"

// chứ không phải "lần lượt"

var sờ_trên = new Promise((resolve, reject) => {

resolve("Phê trên");

});

var sờ_dưới = new Promise((resolve, reject) => {

resolve("Phê dưới");

});

var sờ_tùm_lum = new Promise((resolve, reject) => {

resolve("Phê tùm lum chỗ");

});


// Truyền 1 array chứa toàn bộ promise vào hàm Promise.all

// Hàm này trả ra 1 promise, tổng hợp kết quả của các promise đưa vào

Promise.all([sờ_trên, sờ_dưới, sờ_tùm_lum])

.then(function(result) {

console.log(result); // ["Phê trên", "Phê dưới", "Phê tùm lum chỗ"]

// Phê xong làm gì ai biết

})
view rawpromise_all.js hosted with ❤ by GitHub

Tổng kết

Với các ưu điểm của mình, promise đang dần dần thay thế cho callback. Dĩ nhiên là không thay thế hoàn toàn đâu. Với các hàm đơn giản thì dùng callback sẽ ngắn và dễ hiểu hơn promise.
Promise được sử dụng nhiều trong AngularJS và một số module của NodeJS. Nắm vững khái niệm promise sẽ giúp bạn code đỡ cực hơn nhiều.
Hiện tại, các bạn có thể dùng Promise trong trình duyệtChrome và các bản NodeJS mới nhất. Với các trình duyệt khác, các bạn phải thêm thư viện promise-polyfill thì mới sử dụng được.
Vì khái niệm promise khá là khó tiêu hóa, mình khuyên các bạn nên đọc lại nhiều lần, kết hợp với xem những bài viết khác liên quan tới nó nhé: