Skip to content

Introduction: callbacks #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions 1-js/11-async/01-callbacks/01-animate-circle-callback/task.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@

# Animated circle with callback
# Lingkaran animasi dengan callback

In the task <info:task/animate-circle> an animated growing circle is shown.
Dalam tugas <info:task/animate-circle> sebuah lingkaran animasi di tampilkan.

Now let's say we need not just a circle, but to show a message inside it. The message should appear *after* the animation is complete (the circle is fully grown), otherwise it would look ugly.
Sekarang katakan kalau kita tidak hanya butuh lingkaran saja, tetapi juga butuh menampilkan pesan didalamnya. Pesan tersebut harus muncul *setelah* animasi nya selesai (lingkaran nya bergerak secara penuh), kalau tidak itu akan terlihat tidak bagus.

In the solution of the task, the function `showCircle(cx, cy, radius)` draws the circle, but gives no way to track when it's ready.
Dalam solusi tugas, fungsi `showCircle(cx, cy, radius)` menggambar lingkaran, tetapi tidak memberikan cara untuk melacak-nya ketika sudah selesai.

Add a callback argument: `showCircle(cx, cy, radius, callback)` to be called when the animation is complete. The `callback` should receive the circle `<div>` as an argument.
Tambahkan sebuah argumen callback: `showCircle(cx, cy, radius, callback)` untuk di panggil ketika animasi-nya sudah selesai. `callback` harusnya menerima `<div>` lingkaran sebagain argumen.

Here's the example:
Seperti ini contohnya:

```js
showCircle(150, 150, 100, div => {
Expand All @@ -22,4 +22,4 @@ Demo:

[iframe src="solution" height=260]

Take the solution of the task <info:task/animate-circle> as the base.
Ambil solusinya untuk tugas <info:task/animate-circle> sebagai dasar.
141 changes: 69 additions & 72 deletions 1-js/11-async/01-callbacks/article.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@


# Introduction: callbacks
# Pengenalan: callback

```warn header="We use browser methods here"
To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods; specifically, loading scripts and performing simple document manipulations.
```warn header="Disini kita menggunakan method dari browser"
Untuk menunjukkan penggunaan callback, promise dan konsep abstract lainnya, kita akan menggunakan beberapa method dari browser; khususnya, memuat script dan melakukan manipulasi dokumen sederhana.

If you're not familiar with these methods, and their usage in the examples is confusing, or if you would just like to understand them better, you may want to read a few chapters from the [next part](/document) of the tutorial.
Jika kamu belum terbiasa dengan method ini, dan penggunaanya didalam contoh membuat bingung, atau jika kamu hanya ingin mengerti lebih baik lagi,kamu mungkin mau membaca beberapa bab dari [bagian selanjutnya](/dokumen) tutorial ini.
```

Many actions in JavaScript are *asynchronous*. In other words, we initiate them now, but they finish later.
Banyak action didalam JavaScript yang *asynchronous*. Dengan kata lain, kita inisiasi action tersebut sekarang, tetapi action tersebut selesai-nya nanti.

For instance, we can schedule such actions using `setTimeout`.
Sebagai contoh, kita bisa atur action tersebut menggunakan `setTimeout`.

There are other real-world examples of asynchronous actions, e.g. loading scripts and modules (we'll cover them in later chapters).
Contoh-contoh lain dari action asynchronous di kehidupan nyata, misalnya memuat script dan module (kita akan bahas di bab selanjutnya).

Take a look at the function `loadScript(src)`, that loads a script with the given `src`:
Coba lihat pada fungsi `loadScript(src)`, yang memuat sebuah script dengan pemberian `src`:

```js
function loadScript(src) {
Expand All @@ -24,41 +24,41 @@ function loadScript(src) {
}
```

It appends to the document the new, dynamically created, tag `<script src="…">`, the browser loads and executes it.
Fungsi tersebut menambahkan ke dokumen baru, dibuat secara dinamis, tag `<script src="…">`, browser kemudian memuat dan menjalankannya.

We can use this function like this:
Kita bisa menggunakan fungsi tersebut seperti ini:

```js
// load and execute the script at the given path
// memuat dan menjalankan script sesuai path yang diberikan
loadScript('/my/script.js');
```

The script is executed "asynchronously", as it starts loading starts now, but runs later, when the function has already finished.
Script tersebut dijalankan secara "asynchronously",dimulai dengan memuat nya sekarang, namun dijalankan nanti, ketika fungsi tersebut sudah selesai.

If there's a code below `loadScript(…)`, it doesn't wait until the script loading finishes.
Jika ada kode lain di bawah `loadScript(…)`, maka tidak akan menunggu sampai pemuatan script selesai.

```js
loadScript('/my/script.js');
// the code below loadScript
// doesn't wait for the script loading to finish
// kode lain dibawah loadScript
// tidak akan menunggu sampai pemuatan script selesai
// ...
```

Let's say we need to use the new script as soon as it loads. It declares new functions, and we want to run them.
Katakanlah kita butuh menggunakan script baru segera setelah dimuat. Mendeklarasikan fungsi baru, dan kita mau menjalankannya.

But if we do that immediately after the `loadScript(…)` call, that wouldn't work:
Tetapi jika kita melakukannya secara langsung setelah memanggil `loadScript(…)`, itu tidak akan berfungsi:

```js
loadScript('/my/script.js'); // the script has "function newFunction() {…}"
loadScript('/my/script.js'); // script memiliki "fungsi newFunction() {…}"

*!*
newFunction(); // no such function!
newFunction(); // tidak ada fungsi seperti itu!
*/!*
```

Naturally, the browser probably didn't have time to load the script. As of now, the `loadScript` function doesn't provide a way to track the load completion. The script loads and eventually runs, that's all. But we'd like to know when it happens, to use new functions and variables from that script.
Tentu saja, browser mungkin tidak punya waktu untuk memuat script tersebut. Seperti yang sekarang, fungsi `loadScript` tidak menyediakan sebuah cara untuk melacak selesainya proses pemuatan. script dimuat dan akhirnya berjalan, itu saja. Tetapi kita ingin tahu ketika itu terjadi, untuk menggunakan fungsi baru dan variable dari script itu.

Let's add a `callback` function as a second argument to `loadScript` that should execute when the script loads:
Mari tambahkan sebuah fungsi `callback` sebagai argumen kedua untuk `loadScript` yang seharusnya dijalankan ketika memuat script:

```js
function loadScript(src, *!*callback*/!*) {
Expand All @@ -73,19 +73,19 @@ function loadScript(src, *!*callback*/!*) {
}
```

Now if we want to call new functions from the script, we should write that in the callback:
Sekarang jika kita ingin memanggil fungsi baru dari script, kita harus menulisnya didalam callback:

```js
loadScript('/my/script.js', function() {
// the callback runs after the script is loaded
newFunction(); // so now it works
// callback berjalan setelah script dimuat
newFunction(); // jadi sekarang bisa berfungsi
...
});
```

That's the idea: the second argument is a function (usually anonymous) that runs when the action is completed.
Itu idenya: argumen kedua adalah sebuah fungsi (biasanya anonymous) yang berjalan ketika sebuah action selesai.

Here's a runnable example with a real script:
Ini contoh yang bisa dijalankan dengan script asli:

```js run
function loadScript(src, callback) {
Expand All @@ -98,38 +98,38 @@ function loadScript(src, callback) {
*!*
loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {
alert(`Cool, the ${script.src} is loaded`);
alert( _ ); // function declared in the loaded script
alert( _ ); // fungsi di deklarasikan didalam script yang sudah dimuat
});
*/!*
```

That's called a "callback-based" style of asynchronous programming. A function that does something asynchronously should provide a `callback` argument where we put the function to run after it's complete.
Itu disebut gaya "callback-based" dalam pemrograman asynchronous. Sebuah fungsi yang melakukan sesuatu secara asynchronous harus menyediakan sebuah argumen `callback` dimana kita meletakkan fungsi untuk dijalankan setelah selesai.

Here we did it in `loadScript`, but of course, it's a general approach.
Disini kita melakukannya didalam `loadScript`, tetapi tentu saja, ini pendekatan secara umum.

## Callback in callback
## Callback didalam callback

How can we load two scripts sequentially: the first one, and then the second one after it?
Bagaimana kita bisa memuat dua script secara berurutan: yang pertama, dan kemudian setelahnya yang kedua?

The natural solution would be to put the second `loadScript` call inside the callback, like this:
Solusi alaminya bisa kita letakkan `loadScript` kedua kemudian panggil didalam callback, seperti ini:

```js
loadScript('/my/script.js', function(script) {

alert(`Cool, the ${script.src} is loaded, let's load one more`);
alert(`Keren, ${script.src} sudah dimuat, ayo muat satu lagi`);

*!*
loadScript('/my/script2.js', function(script) {
alert(`Cool, the second script is loaded`);
alert(`Keren, script kedua sudah dimuat`);
});
*/!*

});
```

After the outer `loadScript` is complete, the callback initiates the inner one.
Setelah `loadScript` yang diluar sudah selesai, kemudian callback inisiasi yang didalam.

What if we want one more script...?
Bagaimana jika kita ingin menambahkan satu script lagi...?

```js
loadScript('/my/script.js', function(script) {
Expand All @@ -138,7 +138,7 @@ loadScript('/my/script.js', function(script) {

*!*
loadScript('/my/script3.js', function(script) {
// ...continue after all scripts are loaded
// ...dilanjutkan setelah semua script sudah dimuat
});
*/!*

Expand All @@ -147,13 +147,13 @@ loadScript('/my/script.js', function(script) {
});
```

So, every new action is inside a callback. That's fine for few actions, but not good for many, so we'll see other variants soon.
Jadi, setiap action baru ada didalam callback. Tidak masalah untuk beberapa action, tetapi tidak bagus apabila banyak action, jadi kita akan melihat varian lainnya.

## Handling errors
## Menangani error

In the above examples we didn't consider errors. What if the script loading fails? Our callback should be able to react on that.
Pada contoh diatas kita tidak mempertimbangkan error. Bagaimana jika script yang dimuat gagal? Callback kita harusnya bisa bereaksi terhadap itu.

Here's an improved version of `loadScript` that tracks loading errors:
Ini sebuah versi `loadScript` yang ditingkatkan untuk melacak error saat memuat:

```js run
function loadScript(src, callback) {
Expand All @@ -162,39 +162,39 @@ function loadScript(src, callback) {

*!*
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`Script load error for ${src}`));
script.onerror = () => callback(new Error(`Error memuat script untuk ${src}`));
*/!*

document.head.append(script);
}
```

It calls `callback(null, script)` for successful load and `callback(error)` otherwise.
Itu akan memanggil `callback(null, script)` apabila berhasil memuat dan `callback(error)` jika tidak berhasil.

The usage:
Pengunaanya:
```js
loadScript('/my/script.js', function(error, script) {
if (error) {
// handle error
// menangani error
} else {
// script loaded successfully
// sukses memuat script
}
});
```

Once again, the recipe that we used for `loadScript` is actually quite common. It's called the "error-first callback" style.
Sekali lagi, resep yang kita gunakan untuk `loadScript` sebenarnya cukup umum. Itu disebut gaya "error-first callback".

The convention is:
1. The first argument of the `callback` is reserved for an error if it occurs. Then `callback(err)` is called.
2. The second argument (and the next ones if needed) are for the successful result. Then `callback(null, result1, result2…)` is called.
Ketentuan-nya adalah:
1. Argumen pertama dari `callback` dicadangkan untuk sebuah error jika itu terjadi. Kemudian `callback(err)` dipanggil.
2. Argumen kedua (dan argumen selanjutnya jika dibutuhkan) untuk hasil yang sukses. Kemudian `callback(null, result1, result2…)` dipanggil.

So the single `callback` function is used both for reporting errors and passing back results.
Jadi fungsi `callback` single tersebut keduanya digunakan untuk pelaporan error dan memberikan sebuah hasil.

## Pyramid of Doom

From the first look, it's a viable way of asynchronous coding. And indeed it is. For one or maybe two nested calls it looks fine.
Dari tampilan pertama, itu adalah cara yang layak untuk coding secara asynchronous. Dan memang demikian. Untuk satu atau bahkan dua pemanggilan bersarang itu tidak masalah.

But for multiple asynchronous actions that follow one after another we'll have code like this:
Tetapi untuk beberapa action asynchronous yang mengikuti satu demi satu kita akan punya kode seperti ini:

```js
loadScript('1.js', function(error, script) {
Expand All @@ -213,7 +213,7 @@ loadScript('1.js', function(error, script) {
handleError(error);
} else {
*!*
// ...continue after all scripts are loaded (*)
// ...dilanjutkan setelah semua script dimuat (*)
*/!*
}
});
Expand All @@ -224,17 +224,15 @@ loadScript('1.js', function(error, script) {
});
```

In the code above:
1. We load `1.js`, then if there's no error.
2. We load `2.js`, then if there's no error.
3. We load `3.js`, then if there's no error -- do something else `(*)`.
Dalam kode di atas:
1. Kita memuat `1.js`, kemudian jika tidak ada error.
2. Kita muat `2.js`, kemudian jika tidak ada error.
3. Kita muat `3.js`, jika tidak ada error -- lakukan sesuatu yang lain `(*)`.

As calls become more nested, the code becomes deeper and increasingly more difficult to manage, especially if we have a real code instead of `...`, that may include more loops, conditional statements and so on.
Saat pemanggilan jadi lebih bersarang, kode akan menjadi lebih dalam dan semakin susah untuk diatur, khususnya jika kita punya kode asli daripada `...`, itu mungkin berisi loop yang lebih, pernyataan bersyarat dan seterusnya.

That's sometimes called "callback hell" or "pyramid of doom."
Ini kadang disebut "callback hell" atau "pyramid of doom."

<<<<<<< HEAD
=======
<!--
loadScript('1.js', function(error, script) {
if (error) {
Expand All @@ -259,14 +257,13 @@ loadScript('1.js', function(error, script) {
});
-->

>>>>>>> 8c30654f694fe8682f5631809980be931ee4ed72
![](callback-hell.svg)

The "pyramid" of nested calls grows to the right with every asynchronous action. Soon it spirals out of control.
"Piramida" panggilan bersarang bergerak ke kanan dengan setiap action asynchronous. Segera itu akan lepas kendali.

So this way of coding isn't very good.
Jadi, ini merupakan cara coding yang tidak baik.

We can try to alleviate the problem by making every action a standalone function, like this:
Kita bisa coba untuk meringankan masalah tersebut dengan membuat setiap action menjadi fungsi yang mandiri atau berdiri sendiri, seperti ini:

```js
loadScript('1.js', step1);
Expand All @@ -293,17 +290,17 @@ function step3(error, script) {
if (error) {
handleError(error);
} else {
// ...continue after all scripts are loaded (*)
// ...berlanjut setelah semua script dimuat(*)
}
};
```

See? It does the same, and there's no deep nesting now because we made every action a separate top-level function.
Lihat? Itu sama saja, dan tidak ada sarang yang dalam sekarang karena kita buat setiap action menjadi fungsi top-level yang terpisah.

It works, but the code looks like a torn apart spreadsheet. It's difficult to read, and you probably noticed that one needs to eye-jump between pieces while reading it. That's inconvenient, especially if the reader is not familiar with the code and doesn't know where to eye-jump.
Ini berfungsi, tetapi kode-nya terlihat seperti sebuah spreadsheet yang terkoyak. Itu sulit di baca, dan kamu mungkin memperhatikan kalau yang satu butuh untuk eye-jump antara potongan lainnya saat di baca. Itu tidak nyaman, khususnya jika pembaca tidak terbiasa dengan kode dan tidak tahu harus kemana saat eye-jump.

Also, the functions named `step*` are all of single use, they are created only to avoid the "pyramid of doom." No one is going to reuse them outside of the action chain. So there's a bit of a namespace cluttering here.
Juga, fungsi yang bernama `step*` semuanya digunakan sekali saja, mereka dibuat hanya untuk menghindari "pyramid of doom." Tidak ada satupun yang digunakan kembali di luar rantai action. Jadi ada sedikit namespace yang berantakan disini.

We'd like to have something better.
Kita ingin memiliki sesuatu yang lebih baik.

Luckily, there are other ways to avoid such pyramids. One of the best ways is to use "promises," described in the next chapter.
Untunglah, ada cara lain untuk menghindari pyramida seperti itu. Salah satu cara yang bagus yaitu menggunakan "promise," di jelaskan dalam bab selanjutnya.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy