Daftar isi
Kata PengantarIkhtisar ES71. Objek2. Nilai Objek3. String.prototype.padEnd4. String.prototype.padStart5. Object.getOwnPropertyDescriptor6. Membuntuti koma7. SharedArrayBuffer8. Atom9. Fungsi-fungsi AsyncKata Pengantar
Halo, di masa lalu saya sudah mempertimbangkan inovasi dalam ES6 dan sekarang saatnya untuk membongkar ES8 karena membawa banyak hal baru. Saya tidak mempertimbangkan ES7 (2016) secara terpisah, karena rilis ini hanya membawa 2 inovasi. Ini adalah Array.prototype.includes () dan operator eksponensial. Tapi tetap saja, sebelum memulai ES8, mari kita lihat inovasi dari ES7.
Ikhtisar ES7
Metode include () menentukan apakah array berisi elemen tertentu, mengembalikan benar atau salah tergantung pada ini.
Array.prototype.includes(searchElement[, fromIndex = 0]) : Boolean
searchElement - Item yang akan dicari.
fromIndex - Posisi dalam larik tempat mulai mencari elemen searchElement. Untuk nilai negatif, pencarian dilakukan mulai dengan larik indeks. Panjang + dariIndeks naik. Nilai standarnya adalah 0.
Contohnya [1, 2, 3].includes(2);
mencakup () dapat diterapkan ke jenis objek lainnya (misalnya, objek mirip array). Contoh: menggunakan metode include () pada objek argumen.
(function() { console.log([].includes.call(arguments, 'a'));
Operator eksponensial (**) mengembalikan daya dengan basis a dan eksponen alami b. Meningkatkan ke kekuatan b.
a ** b
Contohnya 2 ** 3
1. Objek
Object.entries () mengembalikan array yang elemen-elemennya adalah array yang sesuai dengan properti enumerated dari pasangan [key, value] yang ditemukan langsung di objek. Urutan properti adalah sama seperti ketika Anda menggilir properti suatu objek secara manual.
Object.entries(obj) : Array
obj - Objek yang properti enumerasinya akan dikembalikan sebagai array [kunci, nilai].
Object.entries () mengembalikan properti dalam urutan yang sama seperti pada for ... in loop (perbedaannya adalah bahwa untuk-in juga mencantumkan properti dari rantai prototipe). Urutan elemen dalam larik yang dikembalikan Object.entries () tidak bergantung pada bagaimana objek dideklarasikan. Jika urutan tertentu diperlukan, maka array harus diurutkan sebelum metode dipanggil.
Contohnya var obj = { foo: "bar", baz: 42 }; console.log(Object.entries(obj));
Konversi Objek ke PetaKonstruktor Map () baru menerima pengulangan nilai. Dengan Object.entries, Anda dapat dengan mudah mengkonversi Obyek ke Peta. Ini lebih ringkas daripada menggunakan array 2 elemen array, tetapi kunci hanya bisa berupa string.
var obj = { foo: "bar", baz: 42 }; var map = new Map(Object.entries(obj)); console.log(map);
Mengapa nilai pengembalian Object.entries () array dan bukan iterator?
Kasus penggunaan yang sesuai dalam kasus ini adalah Object.keys (), dan bukan, misalnya, Map.prototype.entries ().
Mengapa Object.entries () mengembalikan hanya properti enumerasi enumerated dengan kunci string?
Sekali lagi, ini dilakukan untuk mencocokkan Object.keys (). Metode ini juga mengabaikan properti yang kuncinya adalah karakter. Pada akhirnya, mungkin ada metode Reflect.ownEntries () yang mengembalikan semua propertinya sendiri.
Lihat object.entries dalam
spesifikasi resmi, serta di
MDN Web Documents .
2. Nilai Objek
Object.values () mengembalikan array yang elemen-elemennya adalah nilai-nilai dari properti yang disebutkan yang ditemukan dalam objek. Urutannya sama seperti jika Anda menggilir objek secara manual.
Object.values(obj) : Array
obj - Objek yang nilainya dari properti yang disebutkan akan dikembalikan.
Metode Object.values () mengembalikan array nilai properti enumerasi objek dalam urutan yang sama dengan for ... in loop. Perbedaan antara loop dan metode adalah bahwa loop mendaftar properti dari dan dari rantai prototipe.
Contohnya var obj = { foo: "bar", baz: 42 }; console.log(Object.values(obj));
Perbedaan antara Object.entries dan Object.values () adalah bahwa yang pertama mengembalikan array yang berisi nama dan nilai properti, sedangkan yang kedua hanya mengembalikan array dengan nilai properti.
Contoh perbedaan antara Object.values () dan Object.entries () const object = { a: 'somestring', b: 42, c: false }; console.log(Object.values(object));
Lihat Object.values () dalam
spesifikasi resmi, serta di
MDN Web Documents .
3. String.prototype.padEnd
Metode padEnd () melengkapi baris saat ini dengan string yang diberikan (akhirnya berulang) sehingga string yang dihasilkan mencapai panjang yang ditentukan. Penambahan diterapkan di ujung (kanan) dari baris saat ini.
String.prototype.padEnd(maxLength [ , fillString ]) : String
maxLength - Panjang baris yang dihasilkan setelah baris saat ini telah diisi. Jika parameter ini kurang dari panjang garis saat ini, garis saat ini akan dikembalikan apa adanya.
fillString - Sebuah string untuk melengkapi baris saat ini dengan. Jika baris ini terlalu panjang, akan terpotong dan paling kiri akan diterapkan. "" (0x0020 SPACE) adalah nilai default untuk parameter ini.
Contohnya 'abc'.padEnd(10);
Gunakan kasus untuk mengisi string meliputi:
- Menambahkan penghitung atau pengidentifikasi ke nama file atau URL: 'file 001.txt'
- Penyelarasan Keluaran Konsol: “Test 001: ✓”
- Cetak angka heksadesimal atau biner dengan angka digit tetap: '0x00FF'
Lihat String.prototype.padEnd dalam
spesifikasi resmi, serta di
MDN Web Documents .
4. String.prototype.padStart
Metode padStart () mengisi baris saat ini dengan baris lain (beberapa kali, jika perlu) sehingga garis yang dihasilkan mencapai panjang yang ditentukan. Pengisian dilakukan di awal (kiri) dari garis saat ini.
String.prototype.padStart(maxLength [, fillString]) : String
maxLength - Panjang garis ringkasan setelah selesainya garis saat ini. Jika nilainya kurang dari panjang garis saat ini, garis saat ini akan dikembalikan tidak berubah.
fillString - String untuk mengisi baris saat ini. Jika string ini terlalu panjang untuk panjang yang diberikan, itu akan dipotong. Nilai standarnya adalah "" (0x0020 SPACE).
Contohnya 'abc'.padStart(10);
Mengapa metode padding tidak disebut padLeft dan padRight?
Untuk bahasa dua arah atau kanan-ke-kiri, istilah "kiri" dan "kanan" tidak berfungsi. Oleh karena itu, penamaan padStart dan padEnd mengikuti nama-nama yang ada mulai dengan beginWith dan berakhirWith.
Lihat String.prototype.padStart dalam
spesifikasi resmi, serta di
MDN Web Documents .
5. Object.getOwnPropertyDescriptor
Metode Object.getOwnPropertyDescriptor () mengembalikan deskriptor properti untuk propertinya sendiri (yaitu, yang terletak langsung di objek, dan tidak diterima melalui rantai prototipe) dari objek yang dikirimkan. Jika properti tidak ada, pengembalian tidak ditentukan.
Object.getOwnPropertyDescriptor(obj, prop) : Object
obj - Objek di mana properti dicari.
prop - Nama properti yang uraiannya akan dikembalikan.
Metode ini memungkinkan Anda untuk melihat deskripsi properti yang tepat. Properti dalam JavaScript terdiri dari nama string dan deskriptor properti.
Deskriptor properti adalah catatan dengan beberapa atribut berikut:
- value - Nilai yang terkait dengan properti (hanya dalam deskriptor data).
- dapat ditulis - benar jika nilai yang terkait dengan properti dapat diubah, jika tidak palsu (hanya dalam deskriptor data).
- get - Fungsi yang mengembalikan nilai properti, atau tidak terdefinisi jika tidak ada fungsi seperti itu (hanya di deskriptor akses).
- set - Fungsi yang mengubah nilai properti, atau tidak terdefinisi jika tidak ada fungsi seperti itu (hanya di deskriptor akses).
- dapat dikonfigurasi - benar jika jenis pegangan properti ini dapat diubah dan jika properti dapat dihapus dari objek yang mengandungnya, jika tidak palsu.
- enumerable - true jika properti ini tersedia saat mendaftar properti dari objek yang mengandungnya, jika tidak salah.
Contohnya obj = { get foo() { return 10; } }; console.log(Object.getOwnPropertyDescriptor(obj, 'foo'));
Gunakan case untuk Object.getOwnPropertyDescriptor ()
Kasus penggunaan
pertama : menyalin properti ke objek
Dimulai dengan ES6, JavaScript sudah memiliki metode alat untuk menyalin properti: Object.assign (). Namun, metode ini menggunakan sederhana dan mengatur operasi untuk menyalin properti yang kuncinya adalah kunci:
const value = source[key];
Ini berarti bahwa itu tidak benar menyalin properti dengan atribut selain yang ditentukan secara default (metode untuk memperoleh, mengatur, menulis, dll.). Contoh berikut menggambarkan batasan ini. Sumber objek memiliki installer yang kuncinya adalah foo:
const source = { set foo(value) { console.log(value); } }; console.log(Object.getOwnPropertyDescriptor(source, 'foo'));
Menggunakan Object.assign () untuk menyalin properti foo ke objek target gagal:
const target1 = {}; Object.assign(target1, source); console.log(Object.getOwnPropertyDescriptor(target1, 'foo'));
Untungnya, menggunakan Object.getOwnPropertyDescriptors () bersama dengan Object.defineProperties () berfungsi:
const target2 = {}; Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source)); console.log(Object.getOwnPropertyDescriptor(target2, 'foo'));
Kasus penggunaan
kedua : objek kloning
Kloning dangkal mirip dengan menyalin properti, jadi Object.getOwnPropertyDescriptors () juga merupakan pilihan yang baik di sini.
Kali ini kami menggunakan Object.create (), yang memiliki dua parameter:
Parameter pertama menentukan prototipe objek yang dikembalikan.
Parameter kedua opsional adalah kumpulan deskriptor properti, mirip dengan yang dikembalikan oleh Object.getOwnPropertyDescriptors ().
const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Kasus penggunaan
ketiga : objek lintas-platform literal dengan prototipe sewenang-wenang.
Cara terbaik secara sintaksis untuk menggunakan objek literal untuk membuat objek dengan prototipe arbitrer adalah dengan menggunakan properti __proto__ khusus:
const obj = { __proto__: prot, foo: 123, };
Sayangnya, fitur ini dijamin hanya ada di browser. Solusi umum adalah Object.create () dan tugas:
const obj = Object.create(prot); obj.foo = 123;
Tetapi Anda juga dapat menggunakan Object.getOwnPropertyDescriptors ():
const obj = Object.create( prot, Object.getOwnPropertyDescriptors({ foo: 123, }) );
Alternatif lain adalah Object.assign ():
const obj = Object.assign( Object.create(prot), { foo: 123, } );
Pitfall: menyalin metode menggunakan super.
Metode yang super gunakan terikat erat pada objek asalnya (objek tempat penyimpanannya). Saat ini tidak ada cara untuk menyalin atau memindahkan metode seperti itu ke objek lain.
Lihat Object.getOwnPropertyDescriptor dalam
spesifikasi resmi, serta di
MDN Web Documents .
6. Membuntuti koma
Menggantung koma (Trailing koma) - dapat berguna saat menambahkan elemen, parameter, atau properti baru ke kode JavaScript. Jika Anda ingin menambahkan properti baru, Anda cukup menambahkan baris baru tanpa mengubah yang sebelumnya, jika koma menggantung sudah digunakan di dalamnya. Ini membuat perbedaan dalam kontrol versi pembersih dan perubahan kode bisa kurang merepotkan.
Menggantung koma dalam literal
ArrayJavaScript mengabaikan menggantung koma dalam array:
var arr = [ 0, 1, 2, ]; console.log(arr);
Jika lebih dari satu titik menggantung digunakan, lubang akan dibuat. Array dengan "lubang" disebut jarang (array padat tidak memiliki "lubang"). Ketika iterasi array menggunakan, misalnya, Array.prototype.forEach () atau Array.prototype.map (), lubang akan dilewati.
Benda-benda var object = { foo: "bar", baz: "qwerty", age: 42, }; console.log(object);
Menggantung koma dalam fungsi
Definisi ParameterDefinisi parameter fungsi berikut ini valid dan setara satu sama lain. Menggantung koma tidak memengaruhi properti panjang fungsi atau objek argumennya.
function f(p) {} function f(p,) {} (p) => {}; (p,) => {};
Definisi metodeKoma gantung juga berfungsi dengan mendefinisikan metode untuk kelas atau objek.
class C { one(a,) {}, two(a, b,) {}, } var obj = { one(a,) {}, two(a, b,) {}, };
Panggilan fungsiPanggilan fungsi berikut ini valid dan setara satu sama lain.
f(p); f(p,); Math.max(10, 20); Math.max(10, 20,);
Koma gantung tidak validMenentukan parameter fungsi atau memanggil fungsi yang hanya berisi koma akan memunculkan SyntaxError. Selain itu, saat menggunakan parameter yang tersisa, menggantung koma tidak diperbolehkan.
function f(,) {}
Menggantung Koma dalam Destrukturisasi
Menggantung koma juga dapat digunakan di sebelah kiri saat menggunakan tugas yang merusak.
Sekali lagi, menggunakan parameter yang tersisa, sebuah SyntaxError akan dilempar.
var [a, ...b,] = [1, 2, 3];
JSON Menggantung Koma
Menggantung koma di objek hanya diperbolehkan dalam ECMAScript 5. Karena JSON didasarkan pada sintaksis JavaScript yang lebih tua dari ES5,
koma menggantung tidak diizinkan
di JSON.Kedua baris melempar SyntaxError
JSON.parse('[1, 2, 3, 4, ]'); JSON.parse('{"foo" : 1, }');
Mengapa menggantung koma bermanfaat?
Ada dua manfaatnya.
Pertama, mengatur ulang elemen lebih mudah karena Anda tidak perlu menambah atau menghapus koma jika elemen terakhir mengubah posisinya.
Kedua, ini membantu sistem kontrol versi melacak apa yang benar-benar berubah. Misalnya, dari:
[ 'Foo' ] : [ 'Foo', '' ]
menyebabkan baris dengan 'foo' dan baris dengan 'bar' ditandai sebagai diubah, meskipun satu-satunya perubahan nyata adalah menambahkan baris terakhir.
Lihat Melacak koma di
MDN Web Documents .
7. SharedArrayBuffer
Objek SharedArrayBuffer digunakan untuk membuat buffer split dengan panjang tetap untuk menyimpan data biner primitif, mirip dengan objek ArrayBuffer, tetapi sebaliknya, instance SharedArrayBuffer dapat digunakan untuk membuat tampilan pada memori bersama. SharedArrayBuffer tidak dapat diputus.
new SharedArrayBuffer(length) : Object
length - Ukuran, dalam byte, untuk membuat array buffer.
kembali - Objek SharedArrayBuffer baru dengan panjang yang ditentukan. Isinya setelah inisialisasi adalah 0.
PostMessage dan kloning terstruktur digunakan untuk membagi memori menggunakan objek SharedArrayBuffer antara satu agen di cluster dan yang lain (agen dapat berupa program utama halaman web atau salah satu pekerja web).
Algoritma kloning terstruktur menerima SharedArrayBuffers dan TypedArrays yang dipetakan ke SharedArrayBuffers. Dalam kedua kasus, objek SharedArrayBuffer diteruskan ke penerima, yang membuat objek SharedArrayBuffer pribadi baru di dalam agen penerima (sama seperti untuk ArrayBuffer). Namun, blok data bersama yang dirujuk oleh kedua objek SharedArrayBuffer adalah blok data yang sama, dan efek pihak ketiga di blok di salah satu agen akhirnya akan terlihat di agen lain.
var sab = new SharedArrayBuffer(1024); worker.postMessage(sab);
Memori bersama dapat dibuat dan diubah secara bersamaan di pekerja atau utas utama. Bergantung pada sistem (CPU, OS, browser), mungkin perlu waktu hingga perubahan disebarkan ke semua konteks. Untuk sinkronisasi, operasi atom diperlukan.
Buffer Array Bersama adalah blok bangunan primitif untuk abstraksi paralelisme tingkat yang lebih tinggi. Mereka memungkinkan Anda untuk berbagi byte dari objek SharedArrayBuffer antara beberapa pekerja dan utas utama (buffer dibagi untuk mengakses byte, membungkusnya dalam Typed Array). Jenis pertukaran ini memiliki dua keunggulan:
Anda dapat bertukar data antar pekerja lebih cepat.
Koordinasi antar pekerja menjadi lebih mudah dan lebih cepat (dibandingkan dengan postMessage ()).
Implementasi pekerja adalah sebagai berikut.
Pertama, kami mengekstrak buffer array bersama yang dikirim kepada kami, dan kemudian membungkusnya dalam array yang diketik sehingga kami dapat menggunakannya secara lokal.
Properti dan metode SharedArrayBuffer.SharedArrayBuffer.length - Panjang konstruktor SharedArrayBuffer yang nilainya 1.
SharedArrayBuffer.prototype - Memungkinkan properti tambahan untuk semua objek SharedArrayBuffer.
Mesin Virtual SharedArrayBufferSifat-sifatSharedArrayBuffer.prototype.constructor - Menentukan fungsi yang membuat prototipe objek. Nilai awal adalah konstruktor SharedArrayBuffer bawaan bawaan.
SharedArrayBuffer.prototype.byteLength (Hanya baca) - Ukuran array dalam byte. Ini diatur ketika array dibuat dan tidak dapat diubah.
MetodeSharedArrayBuffer.prototype.slice () - Mengembalikan SharedArrayBuffer baru yang isinya adalah salinan dari byte dari SharedArrayBuffer ini dari awal, termasuk hingga akhir, eksklusif. Jika awal atau akhir negatif, ini merujuk pada indeks dari akhir array, bukan dari awal. Metode ini memiliki algoritma yang sama dengan Array.prototype.slice ().
sab.slice([begin, end]) : Object
begin - Indeks nol di mana ekstraksi dimulai. Anda dapat menggunakan indeks negatif yang menunjukkan ofset dari akhir urutan. slice (-2) mengekstraksi dua elemen terakhir secara berurutan. Jika awal tidak ditentukan, irisan dimulai pada indeks 0.
Akhir - Indeks berbasis nol di mana ekstraksi harus diselesaikan.
Misalnya, irisan (1,4) mengambil elemen kedua melalui elemen keempat (elemen dengan indeks 1, 2, dan 3). Anda dapat menggunakan indeks negatif yang menunjukkan ofset dari akhir urutan. slice (2, -1) mengambil elemen ketiga melalui elemen kedua terakhir dalam urutan. Jika ujung dihilangkan, irisan mengambil melalui akhir urutan (sab.byteLength).
Contohnya var sab = new SharedArrayBuffer(1024); sab.slice();
Lihat SharedArrayBuffer dalam
spesifikasi resmi, serta di
MDN Web Documents .
8. Atom
Objek Atom menyediakan operasi atom sebagai metode statis. Digunakan dengan objek SharedArrayBuffer.
Operasi atom dipasang di modul Atomics. Tidak seperti objek global lainnya, Atomics bukan konstruktor. Itu tidak dapat digunakan dengan operator baru atau untuk memanggil objek Atomics sebagai fungsi. Semua properti dan metode Atom adalah statis (seperti objek Matematika, misalnya).
Ketika memori dibagi, beberapa utas dapat membaca dan menulis data yang sama ke memori. Operasi atom menjamin bahwa nilai yang diharapkan akan ditulis dan dibaca, dan operasi selesai sebelum operasi berikutnya dimulai, dan mereka tidak akan terganggu.
Sifat-sifat
Atomics [Symbol.toStringTag] - Nilai properti ini adalah Atomics.
Metode
Operasi atom- Atomics.add () - Menambahkan nilai yang disajikan ke yang sekarang pada posisi yang ditentukan dalam array. Mengembalikan nilai sebelumnya pada posisi ini.
- Atomics.and () - Menghitung bitwise DAN pada posisi array yang ditentukan. Mengembalikan nilai sebelumnya pada posisi ini.
- Atomics.compareExchange () - Menyimpan nilai yang disajikan ke posisi array yang ditentukan, jika setara dengan nilai yang disajikan. Mengembalikan nilai sebelumnya.
- Atomics.exchange() — . .
- Atomics.load() — .
- Atomics.or() — OR . .
- Atomics.store() — . .
- Atomics.sub() — . .
- Atomics.xor() — XOR . .
Metode statis Atomics.add () menambahkan nilai ke yang sekarang pada posisi yang ditentukan dalam array dan mengembalikan nilai sebelumnya pada posisi ini. Operasi atom ini memastikan bahwa tidak ada penulisan lain yang terjadi sampai nilai yang diubah ditulis kembali. Atomics.add(typedArray, index, value) : mixed
- typedArray - Array perpecahan bilangan bulat. Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array atau Uint32Array.
- index - Posisi di typedArray untuk menambah nilai.
- value - Angka yang akan ditambahkan.
- return - Nilai sebelumnya pada posisi yang ditentukan (typedArray [index]).
- Melempar TypeError jika tipe typedArray bukan salah satu dari tipe integer yang valid.
- Melempar TypeError jika tipe typedArray bukan tipe generik.
- Melempar RangeError jika indeks di luar typedArray.
Contohnya var sab = new SharedArrayBuffer(1024); var ta = new Uint8Array(sab); Atomics.add(ta, 0, 12);
Atomics.add () dalam spesifikasi , di MDN Web Documents .Tunggu dan beri tahu
Metode wait () dan wake () dimodelkan berdasarkan futexes (“fast user-space mutex”) Linux dan menyediakan cara untuk menunggu sesaat ketika keadaan tertentu tidak menjadi kenyataan, dan biasanya digunakan sebagai pemblokiran konstruksi.Atomics.wait ()Memeriksa apakah nilai yang masih terwakili terkandung dalam posisi yang ditentukan dari array dan sedang tertunda atau timeout. Pengembalian ok, tidak sama, atau habis waktu. Jika menunggu tidak diizinkan di agen panggilan, maka itu akan menimbulkan kesalahan pengecualian (sebagian besar browser tidak mengizinkan tunggu () di aliran utama browser).- Atomics.wait() — , -. «ok», «not-equal» «timed-out». , ( wait() ).
- Atomics.wake() — , . , .
- Atomics.isLockFree(size) — , . true, ( ). .
Masalah optimasi
Optimalisasi membuat kode tidak dapat diprediksi di antara para pekerja. Dalam utas tunggal, kompiler dapat melakukan optimasi yang memecahkan kode multithreaded.Ambil, misalnya, kode berikut: while (sharedArray [0] === 123);
Dalam utas tunggal, nilai sharedArray [0] tidak pernah berubah selama eksekusi loop (jika sharedArray adalah array atau array yang diketik yang belum diperbaiki dengan cara apa pun). Karena itu, kode dapat dioptimalkan sebagai berikut: const tmp = sharedArray [0]; while (tmp === 123);
Namun, dalam mode multithreaded, optimasi ini tidak memungkinkan kami untuk menggunakan template ini untuk menunggu perubahan yang dibuat di utas lainnya.Contoh lain adalah kode berikut:
Dalam satu utas, Anda dapat mengatur ulang operasi penulisan ini karena tidak ada yang terbaca di antara keduanya. Beberapa utas mengalami masalah saat Anda mengharapkan rekaman dilakukan dalam urutan tertentu:
Jenis optimasi ini membuat hampir tidak mungkin untuk menyinkronkan tindakan beberapa pekerja yang bekerja pada buffer yang sama dengan array umum.Memecahkan masalah optimisasi
Menggunakan variabel global Atomics, yang metodenya memiliki tiga kegunaan utama.Kasus penggunaan pertama: sinkronisasi.Metode atom dapat digunakan untuk menyinkronkan dengan pekerja lain. Misalnya, dua operasi berikut memungkinkan Anda untuk membaca dan menulis data dan tidak pernah disusun ulang oleh kompiler: Atomics.load (TypedArray <T>, index) : T Atomics.store (TypedArray <T>, index, value: T) : T
Idenya adalah menggunakan operasi biasa untuk membaca dan menulis sebagian besar data, sementara operasi Atom (memuat, menyimpan, dan lainnya) memastikan bahwa membaca dan menulis aman. Seringkali Anda akan menggunakan mekanisme sinkronisasi Anda sendiri, seperti kunci, yang didasarkan pada Atomics.Ini adalah contoh yang sangat sederhana yang selalu berfungsi berkat Atomics (Saya melewatkan pengaturan sharedArray):
Kasus penggunaan kedua: menunggu pemberitahuan .Menggunakan loop sementara untuk menunggu pemberitahuan tidak terlalu efisien, sehingga Atomics memiliki operasi yang membantu: Atomics.wait (Int32Array, indeks, nilai, batas waktu) dan Atomics.wake (Int32Array, indeks, hitung).Kasus penggunaan ketiga: operasi atomBeberapa operasi Atom melakukan aritmatika dan tidak dapat terganggu pada saat yang sama, yang membantu sinkronisasi. Sebagai contoh:
Atomics.add (TypedArray <T>, index, value) : T
Secara kasar, operasi ini melakukan: index + = value;Masalah dengan nilai sobekan.Efek masalah lain dengan memori bersama adalah nilai robek (sampah): saat membaca, Anda dapat melihat nilai menengah - baik nilai sebelum nilai baru ditulis ke memori, maupun nilai baru.Bagian Tear-Free Reads dari spesifikasi menyatakan bahwa tidak ada kesenjangan jika dan hanya jika:- Baik membaca dan menulis terjadi melalui Typed Array (bukan DataViews).
- Kedua array yang diketikkan disejajarkan dengan buffer array bersama mereka: sharedArray.byteOffset% sharedArray.BYTES_PER_ELEMENT === 0
- Kedua array yang diketik memiliki jumlah byte yang sama per elemen.
Dengan kata lain, nilai sobek adalah masalah ketika buffer yang sama dari array bersama diakses melalui:- Satu atau lebih DateViews;
- Ada satu atau lebih array yang tidak selaras;
- Array diketik dengan berbagai ukuran elemen;
Untuk menghindari kesenjangan nilai dalam kasus ini, gunakan Atomics atau sinkronisasi.Buffer Array yang Dibagikan dalam Penggunaan
Array yang Dibagikan Buffer dan semantik JavaScript untuk menjalankan fungsi yang tertunda. JavaScript memiliki apa yang disebut semantik eksekusi "sebelum selesai": setiap fungsi dapat berharap bahwa itu tidak akan terganggu oleh utas lain sampai selesai. Fungsi menjadi transaksi dan dapat menjalankan algoritme lengkap, sementara tidak ada yang melihat data yang digunakannya dalam kondisi perantara.Array Bersama Buffer menginterupsi siklus ke penyelesaian (RTC): data yang berfungsi berfungsi dapat diubah oleh utas lainnya selama eksekusi fungsi. Namun, kode sepenuhnya mengontrol apakah pelanggaran RTC ini terjadi: jika tidak menggunakan Buffer Array Bersama, itu aman.Ini kira-kira mirip dengan bagaimana fungsi asinkron melanggar RTC. Di sana Anda mengaktifkan operasi kunci menggunakan kata kunci tunggu.Array Bersama Buffer memungkinkan emscripten untuk mengompilasi pthreads di asm.js. Mengutip halaman dokumentasi emscripten:[En] [Buffer Array Bersama memungkinkan] Emscripten aplikasi untuk berbagi tumpukan memori utama antara pekerja web. Ini bersama dengan primitif untuk atom tingkat rendah dan dukungan futex memungkinkan Emscripten untuk mengimplementasikan dukungan untuk API Pthreads (POSIX threads).[Ru] [Buffer Array Bersama memungkinkan] Emscripten aplikasi untuk berbagi banyak memori utama antara pekerja web. Bersamaan dengan primitif atom tingkat rendah dan dukungan futex, Emscripten memungkinkan dukungan untuk Pthreads API (utas POSIX).Artinya, Anda dapat mengkompilasi kode C dan C ++ multithreaded di asm.js.Ada diskusi yang sedang berlangsung tentang cara terbaik untuk menggunakan multithreading di WebAssembly. Mengingat bahwa pekerja web relatif berat, ada kemungkinan bahwa WebAssembly akan memperkenalkan utas yang ringan. Anda juga dapat melihat bahwa topik sedang menuju masa depan WebAssembly.Tukar data selain bilangan bulat
Saat ini, hanya array bilangan bulat (panjang hingga 32 bit) yang dapat digunakan. Ini berarti bahwa satu-satunya cara untuk berbagi jenis data lain adalah dengan menyandikannya sebagai bilangan bulat. Alat yang dapat membantu termasuk:- TextEncoder dan TextDecoder: yang pertama mengubah string menjadi instance Uint8Array, yang terakhir melakukan yang sebaliknya.
- stringview.js: , . .
- FlatJS: JavaScript (, ) (ArrayBuffer SharedArrayBuffer). JavaScript + FlatJS JavaScript. JavaScript (TypeScript . .) .
- TurboScript: JavaScript- . asm.js WebAssembly.
Pada akhirnya, ada kemungkinan bahwa mekanisme pertukaran data tambahan - tingkat lebih tinggi - akan muncul. Dan eksperimen akan terus mencari tahu seperti apa mekanisme ini seharusnya.Seberapa cepat kode menggunakan buffer Array Bersama berfungsi?Lars T. Hansen menulis dua implementasi dari algoritma Mandelbrot (seperti yang dijelaskan dalam artikelnya “ A Taste of JavaScript's New Parallel Primitives ”, versi berurutan dan versi paralel yang menggunakan beberapa pekerja web. Hingga 4 pekerja web dan, karenanya, inti prosesor, akselerasi meningkat hampir secara linear, dari 6,9 frame per detik (1 pekerja web) menjadi 25,4 frame per detik (4 pekerja web). Lebih banyak pekerja web membawa peningkatan produktivitas tambahan, tetapi lebih sederhana.Hansen mencatat bahwa akselerasinya sangat mengesankan, tetapi pekerjaan paralelnya disebabkan oleh kode yang lebih kompleks.Informasi tambahan tentang Buffer Array Bersama dan teknologi pendukung:Teknologi konkurensi JavaScript lainnya:Lihat Objek Atom dalam spesifikasi resmi , serta di MDN Web Documents .9. Fungsi-fungsi Async
Membuat fungsi Async menggunakan konstruktor AsyncFunction
Konstruktor AsyncFunction menciptakan objek fungsi async baru. Dalam JavaScript, setiap fungsi asinkron sebenarnya adalah objek AsyncFunction.Perhatikan bahwa AsyncFunction bukan objek global. Itu dapat diperoleh dengan mengeksekusi kode berikut. Object.getPrototypeOf(async function(){}).constructor
Sintaks new AsyncFunction([arg1[, arg2[, ...argN]],] functionBody)
arg1, arg2, ... argN - Nama yang digunakan oleh fungsi sebagai nama argumen formal. Setiap nama harus berupa string yang cocok dengan pengidentifikasi JavaScript yang valid atau daftar string yang dipisahkan koma; misalnya, "x," "theValue," atau "a, b."functionBody - String yang berisi definisi fungsi dalam kode sumber JavaScript.Objek fungsi Async yang dibuat dengan konstruktor AsyncFunction akan diuraikan saat fungsi tersebut dibuat. Ini kurang efisien daripada mendeklarasikan fungsi asinkron menggunakan ekspresi fungsi async dan memanggilnya di dalam kode Anda, karena fungsi tersebut diuraikan dengan sisa kode.Semua argumen yang diteruskan ke fungsi diperlakukan sebagai nama pengidentifikasi parameter dalam fungsi yang dibuat dalam urutan yang dilewatkan.Memanggil konstruktor AsyncFunction sebagai fungsi (tanpa menggunakan operator baru) memiliki efek yang sama dengan memanggilnya sebagai konstruktor.Fungsi-fungsi Async yang dibuat menggunakan konstruktor AsyncFunction tidak membuat hubungan pendek ke konteks yang membuatnya; Mereka selalu dibuat dalam lingkup global. Ketika mereka mulai, mereka akan dapat mengakses hanya variabel lokal dan variabel global, tetapi tidak memiliki akses ke ruang lingkup di mana konstruktor AsyncFunction dipanggil. Ini berbeda dari menggunakan eval dengan kode untuk fungsi async.Contoh membuat fungsi async menggunakan konstruktor AsyncFunction function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor var a = new AsyncFunction('a', 'b', 'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);'); a(10, 20).then(v => { console.log(v);
Deklarasi fungsi Async
Deklarasi fungsi async mendefinisikan fungsi asinkron yang mengembalikan objek AsyncFunction. Anda juga dapat menentukan fungsi async menggunakan ekspresi fungsi async.Sintaks async function name([param[, param[, ... param]]]) {
name - Nama fungsi.param - Nama argumen yang akan diteruskan ke fungsi.pernyataan - Ekspresi yang mengandung tubuh fungsi.Setelah panggilan, fungsi async mengembalikan Janji. Ketika hasilnya telah diterima, Janji telah selesai, mengembalikan nilai yang diterima. Saat fungsi async melempar pengecualian, Promise akan gagal dengan nilai throws.Fungsi async dapat berisi ekspresi menunggu yang menghentikan sementara eksekusi fungsi async dan menunggu respons dari Janji yang disahkan, kemudian melanjutkan fungsi async dan mengembalikan nilai yang diterima.Kata kunci yang menunggu hanya valid dalam fungsi asinkron. Dalam konteks lain, Anda akan mendapatkan kesalahan SyntaxError.Tujuan dari fungsi async / await adalah untuk menyederhanakan penggunaan janji secara serempak dan untuk mereproduksi beberapa tindakan pada kelompok Janji. Sama seperti Janji adalah seperti panggilan balik terstruktur, async / menunggu seperti kombinasi generator dan janji.Contoh function resolveAfter2Seconds(x) { return new Promise(resolve => { setTimeout(() => { resolve(x); }, 2000); }); } async function add1(x) { const a = await resolveAfter2Seconds(20); const b = await resolveAfter2Seconds(30); return x + a + b; } add1(10).then(v => { console.log(v);
Untuk apa asinkron?
Anda dapat menulis program JS Anda dalam satu file .js, tetapi kode Anda kemungkinan besar akan dipecah menjadi beberapa bagian. Dan hanya satu bagian yang akan dieksekusi sekarang, dan sisanya akan dieksekusi nanti. Fungsi adalah teknik yang paling sering digunakan untuk membagi program menjadi beberapa bagian.Masalah utama sebagian besar pengembang yang melihat JS untuk pertama kalinya adalah kurangnya pemahaman tentang apa yang tidak akan terjadi segera setelah sekarang. Dengan kata lain, tugas-tugas yang tidak dapat diselesaikan sekarang, menurut definisi, akan berakhir secara tidak sinkron. Dan kita tidak akan memiliki perilaku pemblokiran dari program yang kita asumsikan. (You-Dont-Know-JS / async & kinerja, Jake Archibald).
Apa kesalahannya di sini? console.log () dijalankan sebelum kami menerima data dari permintaan.Keputusan yang jelas untuk "menunggu" dari sekarang hingga nanti adalah menggunakan panggilan balik: ajax( "http://some.url.1", function myCallbackFunction(data){ console.log( data );
Pertimbangkan berbagai metode untuk menyelesaikan eksekusi kode sinkron sebelum waktunya.Kami memiliki 3 fungsi getUser, getPosts, getComments. const { getUser, getPosts, getComments } = require('./db'); getUser(1, (error, user) => { if(error) return console.error(error); getPosts(user.id, (error, posts) => { if(error) return console.error(error); getComments(posts[0].id, (error, comment) => { if(error) return console.error(error); console.log(comments); }); }); });
Dalam contoh ini, sulit untuk tidak melihat piramida, yang meningkat dengan penambahan fungsi baru ke dalamnya. Gaya pengkodean ini biasa disebut Callback Hell . Ini adalah pola tertentu yang memberi Anda kontrol atas permintaan (asinkron) yang bersaing, yang memastikan urutan eksekusi mereka.Bagian dari solusi untuk masalah fungsi bersarang adalah dengan menggunakan Promise (yang saya bahas dalam artikel terakhir saya , yang menghapusnya dan membuat kode lebih bersih. Mereka juga menyediakan cara yang lebih mudah untuk menangani kesalahan. Tetapi banyak yang tidak menyukai sintaks ini. getUser(1) .then(user => getPosts(user,id)) .then(posts => getComments(posts[0].id)) .then(comments => console.log(comments)) .catch(error => console.error(error));
Generator menjadi alternatif untuk Promise (yang juga saya periksa di artikel sebelumnya . Generator itu sendiri tidak cocok untuk menulis kode asinkron, tetapi jika Anda menggunakannya bersama-sama dengan Promise, kami mendapatkan sesuatu yang unik - kode asinkron yang terlihat sinkron. Pada saat yang sama, generator menyediakan mekanisme penanganan kesalahan yang umum dengan menggunakan coba ... tangkap konstruk. Hanya generator yang memiliki satu minus besar - untuk menggunakannya dengan Janji Anda akan memerlukan fungsi terpisah yang akan mengontrol proses generator A. Anda dapat menulis fungsi ini sendiri atau menggunakan perpustakaan pihak ketiga, misalnya co . Dalam contoh ini, saya menulis implementasi saya dari fungsi tersebut. co(function* () { try { let user = yield getUser(1); let posts = yield getPosts(user.id); let comments = yield getComments(posts[0].id); console.log(comments); } catch (error) { console.log(error); } }); function co(generator) { const iterator = generator(); return new Promise((resolve, reject) => { function run(prev) { const { value, done } = iterator.next(prev); if (done) resolve(value); else if (value instanceof Promise) value.then(run, reject); else run(value); } run(); }); }
Setiap metode bekerja dengan kode asinkron memiliki kelebihan dan kekurangan.Fungsi panggilan balik (fungsi Calback) - Mudah digunakan, tetapi dengan peningkatan fungsi yang disarangkan, keterbacaan mulai berkurang.Promises (Promises) - Elegan dan nyaman, tetapi sulit bagi pemula untuk mengerti.Generator (Generator) - Memungkinkan Anda untuk menulis kode asinkron secara serempak, tetapi mereka memerlukan fungsi terpisah, dan mekanisme pengoperasian generator sangat rumit.Fungsi asinkron dibuat berdasarkan Janji dan Generator, untuk membuat bekerja dengan kode asinkron menjadi sederhana dan mudah dimengerti.Untuk memahami apa fungsi asinkron, pertimbangkan contoh berikut: function getUser(id) { return { id: 1 }; } let user = getUser(1); console.log(user);
Sekarang jika Anda membuat fungsi asinkron (menambahkan kata kunci async), fungsi tersebut akan mengembalikan Janji yang berisi objek dengan properti id. async function getUser(id) { return { id: 1 }; } let user = getUser(1); console.log(user);
Dengan demikian, kita dapat mengatakan bahwa fungsi asinkron mengembalikan Promis (atau lebih tepatnya membungkus nilai Promis yang seharusnya dikembalikan). Jika nilai yang dikembalikan ke fungsi asinkron sudah menjadi janji, maka itu tidak akan dibalik lagi.Untuk mendapatkan nilai dari janji, kita dapat menggunakan metode then (). async function getUser(id) { return { id: 1 }; } getUser(1) .then(user => console.log(user));
Atau kita bisa menggunakan kata kunci tunggu, yang akan dibahas nanti.Mari kita kembali ke contoh pertama kita (hanya kali ini kita akan menggunakan fungsi sebenarnya untuk mengirim permintaan HTTP. fetch(`https://jsonplaceholder.typicode.com/users/1`) .then(data => data.json()) .then(data => console.log(data));
Ini adalah kode asynchronous yang terlihat seperti menggunakan Promise.Tetapi kita dapat menulis kode asinkron sebagai sinkron jika kita menggunakan fungsi asinkron. async function sendRequest() { let response= await fetch(`https://jsonplaceholder.typicode.com/users/1`); return response.json(); } async function main() { var a = await sendRequest(); console.log(a); } main();
Satu-satunya hal yang saya tidak suka adalah bahwa operator async hanya dapat digunakan dalam fungsi asinkron. Kalau tidak, saya tidak perlu menggunakan fungsi main (). Tentu saja, Anda juga dapat menggunakan metode then (), tetapi kemudian kode tersebut tidak lagi terlihat asinkron. async function sendRequest() { let response= await fetch(`https://jsonplaceholder.typicode.com/users/1`); return response.json(); } sendRequest() .then((data) => console.log(data));
Intinya adalah bahwa kita tidak menggunakan fungsi panggilan balik untuk mendapatkan data dari fetch (). Sebagai gantinya, kami menggunakan kata kunci tunggu, yang tampaknya memberi tahu runtime: tunggu fungsi fetch () untuk mengeksekusi dan menulis hasilnya ke variabel respons. Dan menggunakan fungsi callback, kita katakan: tunggu untuk fungsi fetch () untuk mengeksekusi dan memanggil fungsi callback untuk memproses data.Inilah perbedaan yang jelas antara menggunakan fungsi Promise dan async
Operator yang menunggu hanya dapat digunakan dalam tubuh fungsi asinkron, dan tindakannya dapat digunakan pada fungsi apa pun yang mengembalikan janji.Untuk menangani pengecualian dalam fungsi-fungsi asinkron, biasanya digunakan konstruksi coba ... tangkap. async function sendRequest() { let response = await fetch(`https://jsonplaceholder.typicode.com/users/1`); try { throw new Error("Unexpected error"); return response.json(); } catch(error) { console.log(error);
Dan akhirnya ...
Lihat Definisi Fungsi Async dalam spesifikasi resmi , serta di MDN Web Documents .