Asynchronous Process

Secara default, proses pada JavaScript merupakan proses-proses yang bersifat Synchronous, yaitu kode program dieksekusi dari baris atas sampai ke bawah secara berurutan. Namun ada beberapa fungsi pada JavaScript yang bersifat Asynchronous, salah satunya yaitu setTimeout

In [2]:
setTimeout(function() {
    // kode program di dalam sini akan dieksekusi setelah 2000 milidetik (atau 2 detik)
    console.log("This is first");
}, 2000);

setTimeout(function() {
    // kode program di dalam sini akan dieksekusi setelah 1000 milidetik (atau 1 detik)
    console.log("This is second");
}, 1000);

console.log("This is third");
This is third
This is second
This is first

Callback

Jika ingin mengeksekusi program di atas secara berurutan, maka kita harus menaruh kode program di dalam callback. Callback merupakan fungsi yang dieksekusi di akhir jalannya suatu fungsi yang lain. Fungsi setTimeout(Callback, Number) menerima parameter pertama berupa callback

In [3]:
setTimeout(function() {
    // kode program di dalam sini akan dieksekusi setelah 2000 milidetik (atau 2 detik)
    console.log("This is first");
    
    setTimeout(function() {
        // kode program di dalam sini akan dieksekusi setelah 1000 milidetik (atau 1 detik)
        
        console.log("This is second");
        console.log("This is third");
    
    }, 1000);
    
}, 2000);

console.log(); // abaikan baris kode ini
This is first
This is second
This is third

Kamu pasti akan menemukan pola kode program yang banyak menggunakan callback seperti ini ketika membuat program dengan Node.js. Jika callback yang digunakan terlalu banyak dan dalam, maka akan terjadi yang disebut sebagai Callback Hell.

Salah satu cara untuk menyelesaikan masalah tersebut adalah dengan menggunakan Promise

Promise

Promise merupakan suatu objek yang dapat membuat suatu fungsi yang Asynchronous bersifat menjadi Synchronous dengan menggunakan then. Fungsi lain yang bersifat Asynchronous adalah fetch. fetch merupakan fungsi yang digunakan untuk melakukan network request berdasarkan url yang diberikan. Namun kita dapat menggunakan promise pada fetch karena nilai balikannya berupa promise. Sehingga kita dapat merantainya dengan then

In [4]:
var fetch = require("node-fetch"); // baris kode ini boleh tidak ditulis jika dieksekusi di browser

function do_network_request() {
       fetch("https://jsonplaceholder.typicode.com/todos?id=1")
        .then(function(response) {
            return response.json(); // mengambil response body dalam bentuk json
        })
        .then(function(json) {
            console.log(typeof(json));
            console.log(json);
        });
}

do_network_request();
object
[ { userId: 1,
    id: 1,
    title: 'delectus aut autem',
    completed: false } ]

Async/Await

Cara lain agar membuat fungsi Asynchronous menjadi Synchronous adalah dengan menggunakan Async/Await. Gunakan keyword async pada fungsi yang ada proses yang bersifat Asynchronous dan berupa promise, lalu tambahkan keyword await pada proses yang nilai balikannya berupa promise tersebut.

In [5]:
async function do_network_request() {
    var response = await fetch("https://jsonplaceholder.typicode.com/todos?id=2");
    var json = await response.json();
    console.log(json);
}

do_network_request();
[ { userId: 1,
    id: 2,
    title: 'quis ut nam facilis et officia qui',
    completed: false } ]

Deep Dive on Promises

Promise Flow

Promise Signature

In [6]:
new Promise((resolve, reject) => {
        const error = false;
        if (!error) {
            resolve('success'); // nilai 'success' akan tersedia pada .then
        }
        else {
            reject('failed'); // nilai 'failed' akan tersedia pada .catch
        }
    })
    .then(result => {
        console.log(result);
    })
    .catch(error => {
        console.log(error);
    });
success

Promise Chaining

In [7]:
// avoiding callback hell with promise chaining
new Promise((resolve, reject) => {
        setTimeout(() => {
            const counter = 1;
            console.log('counter 1');
            resolve(counter);
        }, 1500);
    })
    .then(counter => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('counter 2');
                resolve(counter + 1);
            }, 1500);
        });
    })
    .then(counter => {
        console.log('last counter');
        console.log(counter + 1);
    });
counter 1
counter 2
last counter
3

Conditional Promise Chaining

In [8]:
// conditional promise chaining
function promisedRedirect(isLoggedIn) {
    let promiseChain = Promise.resolve();

    if (isLoggedIn) {
        promiseChain = new Promise((resolve, reject) => {
           resolve('redirecting to home page'); 
        });
    }
    else {
        promiseChain = new Promise((resolve, reject) => {
           resolve('redirecting to login page'); 
        });
    }
    
    return promiseChain;
}

promisedRedirect(false)
    .then(res => {
        console.log(res);
    });
redirecting to login page

Execute Multiple Promises Asynchronously using Promise.all()

In [9]:
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise1 result');
    }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise2 result');
    }, 2000);
});


const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise3 result');
    }, 6000);
});

const promises = Promise.all([promise1, promise2, promise3]);
promises
    .then(results => {
       console.log(results); 
    });
[ 'promise1 result', 'promise2 result', 'promise3 result' ]

Promise.all akan langsung mengembalikan nilai error jika salah satu promise yang dieksekusinya error

In [10]:
const promise4 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise4 result');
    }, 1000);
});

const promise5 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('promise5 error');
    }, 2000);
});

const promise6 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('promise6 result');
    }, 6000);
});

const promises2 = Promise.all([promise4, promise5, promise6]);
promises2
    .then(results => {
        console.log(results); 
    })
    .catch(error => {
        console.log(error); // nilai balikan berupa error yang dikirim dari promise yang error
    });
promise5 error

Execute Multiple Promises Asynchronously using Promise.allSettled()

Bedanya dengan Promise.all adalah allSettled akan mengembalikan nilai setelah semua promise selesai dieksekusi meskipun terjadi error pada salah satu promise-nya

In [ ]:
const promise7 = Promise.resolve(3);
const promise8 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('kurang ganteng');
    }, 1000);
});

// allSettled is available on node > 12.x.x
const promises3 = Promise.allSettled([promise7, promise8]);
promises3
    .then(results => {
        console.log(results);
    });
In [ ]: