cari

Rumah  >  Soal Jawab  >  teks badan

Adakah pembolehubah kekal tidak berubah selepas pengubahsuaian dalam fungsi? - Rujukan kod tak segerak

<p>鉴于以下示例,为什么 <kod>outerScopeVar</code> 在所有情况下均未定义?</p> <pre class="brush:php;toolbar:false;">var outerScopeVar; var img = document.createElement('img'); img.onload = function() { outerScopeVar = this.width; }; img.src = 'lolcat.png'; alert(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">var outerScopeVar; setTimeout(fungsi() { outerScopeVar = 'Hello Dunia Asynchronous!'; }, 0); alert(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">// Contoh menggunakan beberapa jQuery var outerScopeVar; $.post('lolddog', function(respons) { outerScopeVar = respons; }); alert(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">// Contoh Node.js var outerScopeVar; fs.readFile('./catdog.html', function(err, data) { outerScopeVar = data; }); console.log(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">// dengan janji var outerScopeVar; myPromise.then(fungsi (tindak balas) { outerScopeVar = respons; }); console.log(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">// dengan pemerhatian var outerScopeVar; myObservable.subscribe(fungsi (nilai) { outerScopeVar = nilai; }); console.log(outerScopeVar);</pre> <pre class="brush:php;toolbar:false;">// geolokasi API var outerScopeVar; navigator.geolocation.getCurrentPosition(fungsi (pos) { outerScopeVar = pos; }); console.log(outerScopeVar);</pre> <p>为什么在所有这些示例中都输出 <kod>undefined</code> ?我不需要解决方法,我想知道<strong>为什么</strong>发生这种情况。</p> <hr /> <blockquote> <p><strong>注意:</strong>这是一个关于 JavaScript 异步性的规范问题。请随意改进这个年认同的更多简化示例。</p> </blockquote><p><br /></p>
P粉403804844P粉403804844462 hari yang lalu443

membalas semua(2)saya akan balas

  • P粉064448449

    P粉0644484492023-08-29 11:02:10

    Jawapan Fabrício sangat betul; tetapi saya ingin menambah jawapannya dengan sesuatu yang kurang teknikal, memfokuskan untuk membantu menjelaskan konsep asynchronicity melalui analogi.


    Analogi...

    Semalam, saya perlu mendapatkan beberapa maklumat daripada rakan sekerja saya untuk kerja yang saya lakukan. Saya menelefonnya; perbualannya begini:

    Pada ketika ini, saya meletakkan telefon. Memandangkan saya memerlukan maklumat daripada Bob untuk melengkapkan laporan saya, saya meninggalkan laporan itu, pergi untuk mendapatkan secawan kopi, dan kemudian saya membaca beberapa e-mel. 40 minit kemudian (Bob lambat) Bob menelefon semula dan memberi saya maklumat yang saya perlukan. Pada ketika ini, saya terus membuat laporan saya kerana saya mempunyai semua maklumat yang saya perlukan.


    Bayangkan kalau perbualan jadi begini;

    Saya duduk di sana dan menunggu. dan menunggu. dan menunggu. 40 minit. Buat apa-apa kecuali tunggu. Akhirnya, Bob memberi saya maklumat, kami menutup telefon, dan saya menyelesaikan laporan saya. Tetapi saya kehilangan 40 minit produktiviti.


    Ini ialah tingkah laku tak segerak vs segerak

    Inilah yang berlaku dalam semua contoh dalam soalan kami. Memuatkan imej, memuatkan fail dari cakera, dan meminta halaman melalui AJAX semuanya adalah operasi yang perlahan (dalam konteks pengkomputeran moden).

    JavaScript membolehkan anda mendaftarkan fungsi panggil balik yang akan dilaksanakan apabila operasi perlahan selesai, bukannya menunggu untuk operasi perlahan ini selesai. Tetapi dalam masa yang sama, JavaScript akan terus melaksanakan kod lain. Hakikat bahawa JavaScript melaksanakan kod lain sementara menunggu operasi perlahan selesai menjadikan tingkah laku tak segerak. Jika JavaScript menunggu operasi selesai sebelum melaksanakan sebarang kod lain, ini akan menjadi gelagat segerak.

    var outerScopeVar;    
    var img = document.createElement('img');
    
    // Here we register the callback function.
    img.onload = function() {
        // Code within this function will be executed once the image has loaded.
        outerScopeVar = this.width;
    };
    
    // But, while the image is loading, JavaScript continues executing, and
    // processes the following lines of JavaScript.
    img.src = 'lolcat.png';
    alert(outerScopeVar);

    Dalam kod di atas, kami meminta JavaScript untuk memuatkan lolcat.png, yang merupakan operasi lolcat.png,这是一个sloooow 操作。一旦这个缓慢的操作完成,回调函数就会被执行,但与此同时,JavaScript 将继续处理下一行代码;即alert(outerScopeVar)sloooow

    . Setelah operasi perlahan ini selesai, fungsi panggil balik akan dilaksanakan, tetapi dalam masa yang sama, JavaScript akan terus memproses baris kod seterusnya iaitu, alert(outerScopeVar).

    未定义;因为 alert()Itulah sebabnya kami melihat amaran menunjukkan

    diproses serta-merta, bukan selepas imej dimuatkan.

    alert(outerScopeVar) 代码移到回调函数中。因此,我们不再需要将 outerScopeVarUntuk membetulkan kod kami, yang perlu kami lakukan ialah mengisytiharkan pembolehubah

    sebagai pembolehubah global.

    var img = document.createElement('img');
    
    img.onload = function() {
        var localScopeVar = this.width;
        alert(localScopeVar);
    };
    
    img.src = 'lolcat.png';
    Anda akan sentiasa

    melihat panggilan balik yang ditentukan sebagai fungsi kerana itu satu-satunya cara dalam JavaScript untuk mentakrifkan beberapa kod tetapi kemudian melaksanakannya kemudian.

    function() { /* Do Something */ }Jadi, dalam semua contoh kami, ialah panggilan balik; untuk membetulkan semua

    ​​contoh, yang perlu kita lakukan ialah mengalihkan kod yang memerlukan tindak balas tindakan di sana! 🎜

    * Secara teknikal anda juga boleh menggunakan eval(),但是eval() kejahatan untuk tujuan ini


    Bagaimana untuk menahan pemanggil?

    Anda mungkin mempunyai beberapa kod yang serupa dengan ini;

    function getWidthOfImage(src) {
        var outerScopeVar;
    
        var img = document.createElement('img');
        img.onload = function() {
            outerScopeVar = this.width;
        };
        img.src = src;
        return outerScopeVar;
    }
    
    var width = getWidthOfImage('lolcat.png');
    alert(width);

    Tetapi, kita tahu sekarang 返回outerScopeVar会立即发生;在 onload 回调函数更新变量之前。这会导致 getWidthOfImage() 返回 undefined,并发出警报 undefined.

    Untuk membetulkannya, kita perlu membenarkan fungsi memanggil getWidthOfImage() untuk mendaftarkan panggilan balik dan kemudian mengalihkan amaran lebar di dalam panggilan balik itu

    function getWidthOfImage(src, cb) {     
        var img = document.createElement('img');
        img.onload = function() {
            cb(this.width);
        };
        img.src = src;
    }
    
    getWidthOfImage('lolcat.png', function (width) {
        alert(width);
    });

    ...Seperti sebelum ini, ambil perhatian bahawa kami telah dapat mengalih keluar pembolehubah global (dalam kes ini width).

    balas
    0
  • P粉191610580

    P粉1916105802023-08-29 10:42:58

    Jawapan satu perkataan: Asynchronicity.

    Kata Pengantar

    Topik ini telah diulang sekurang-kurangnya beribu kali pada Stack Overflow. Jadi pertama saya ingin menunjukkan beberapa sumber yang sangat berguna:


    Jawapan kepada soalan semasa

    Mari kita jejak tingkah laku biasa dahulu. Dalam semua contoh, outerScopeVar diubah suai di dalam fungsi . Fungsi ini jelas tidak dilaksanakan serta-merta; ia ditetapkan atau diluluskan sebagai parameter. Inilah yang kami panggil panggilan balik.

    Sekarang persoalannya, bila panggilan balik ini dipanggil?

    Ia bergantung pada situasi tertentu. Mari cuba jejak beberapa gelagat biasa sekali lagi:

    • img.onload Mungkin dipanggil pada masa akan datang (jika) imej telah berjaya dimuatkan.
    • setTimeout boleh dipanggil setTimeout 可能会在延迟到期且超时尚未被 clearTimeout 取消后在将来的某个时间被调用。注意:即使使用 0suatu masa pada masa hadapan
    • selepas kelewatan telah tamat dan tamat masa tidak dibatalkan oleh clearTimeout. Nota: Walaupun semasa menggunakan 0 sebagai kelewatan, semua penyemak imbas mempunyai had atas pada kelewatan tamat masa minimum (dinyatakan sebagai 4 milisaat dalam spesifikasi HTML5).
    • Apabila (dan jika) permintaan Ajax telah berjaya diselesaikan, pada masa hadapan $.post panggilan balik jQuery
    • boleh dipanggil.
    • fs.readFileNode.js' boleh dipanggil pada masa akan datang
    • apabila fail telah berjaya dibaca atau ralat dilemparkan.

    Dalam semua kes, kami mempunyai panggilan balik yang mungkin dijalankan kadang-kadang pada masa hadapan. "Kadang-kadang pada masa hadapan" inilah yang kami panggil aliran tak segerak

    .

    Pelaksanaan tak segerak ditolak keluar daripada proses segerak. Iaitu, semasa tindanan kod segerak sedang dilaksanakan, kod tak segerak akan selamanya

    dilaksanakan. Inilah yang JavaScript adalah thread tunggal.

    Lebih khusus lagi, apabila enjin JS melahu - tidak melaksanakan sekumpulan (a)kod segerak - ia akan meninjau peristiwa yang mungkin mencetuskan panggilan balik tak segerak (cth. tamat masa, respons rangkaian diterima) dan melaksanakannya satu demi satu. Ini dianggap sebagai gelung peristiwa

    .

    Iaitu, kod tak segerak yang diserlahkan dalam bentuk merah lukisan tangan hanya boleh dilaksanakan selepas semua kod segerak yang tinggal dalam blok kod masing-masing telah dilaksanakan:

    Ringkasnya, fungsi panggil balik dicipta secara serentak tetapi dilaksanakan secara tidak segerak. Anda tidak boleh bergantung pada pelaksanaan fungsi async sehingga anda tahu ia telah dilaksanakan, bagaimana anda melakukan ini? 🎜

    Sangat mudah. Logik yang bergantung pada pelaksanaan fungsi async harus dimulakan/dipanggil dari dalam fungsi async tersebut. Contohnya, memindahkan alertconsole.log di dalam fungsi panggil balik akan mengeluarkan hasil yang dijangkakan kerana hasilnya tersedia pada masa itu.

    Laksanakan logik panggil balik anda sendiri

    Selalunya anda perlu melakukan lebih banyak operasi pada hasil fungsi async, atau melakukan operasi yang berbeza pada hasil bergantung pada tempat fungsi async dipanggil. Mari kita berurusan dengan contoh yang lebih kompleks:

    var outerScopeVar;
    helloCatAsync();
    alert(outerScopeVar);
    
    function helloCatAsync() {
        setTimeout(function() {
            outerScopeVar = 'Nya';
        }, Math.random() * 2000);
    }

    Nota: Saya menggunakan setTimeout dengan kelewatan rawak sebagai fungsi async generik; contoh yang sama berfungsi untuk Ajax, readFile, onload dan sebarang aliran tak segerak yang lain.

    Contoh ini nampaknya mempunyai masalah yang sama seperti contoh lain; ia tidak menunggu sehingga fungsi async dilaksanakan.

    Mari selesaikan masalah ini dengan melaksanakan sistem panggilan balik kami sendiri. Mula-mula, kita hapuskan outerScopeVar yang hodoh itu, yang sama sekali tidak berguna dalam kes ini. Kemudian kami menambah parameter yang menerima hujah fungsi, panggilan balik kami. Apabila operasi tak segerak selesai, kami memanggil panggilan balik ini dan lulus hasilnya. Perlaksanaan (sila baca komen mengikut urutan):

    // 1. Call helloCatAsync passing a callback function,
    //    which will be called receiving the result from the async operation
    helloCatAsync(function(result) {
        // 5. Received the result from the async function,
        //    now do whatever you want with it:
        alert(result);
    });
    
    // 2. The "callback" parameter is a reference to the function which
    //    was passed as an argument from the helloCatAsync call
    function helloCatAsync(callback) {
        // 3. Start async operation:
        setTimeout(function() {
            // 4. Finished async operation,
            //    call the callback, passing the result as an argument
            callback('Nya');
        }, Math.random() * 2000);
    }

    Coretan kod untuk contoh di atas:

    // 1. Call helloCatAsync passing a callback function,
    //    which will be called receiving the result from the async operation
    console.log("1. function called...")
    helloCatAsync(function(result) {
        // 5. Received the result from the async function,
        //    now do whatever you want with it:
        console.log("5. result is: ", result);
    });
    
    // 2. The "callback" parameter is a reference to the function which
    //    was passed as an argument from the helloCatAsync call
    function helloCatAsync(callback) {
        console.log("2. callback here is the function passed as argument above...")
        // 3. Start async operation:
        setTimeout(function() {
        console.log("3. start async operation...")
        console.log("4. finished async operation, calling the callback, passing the result...")
            // 4. Finished async operation,
            //    call the callback passing the result as argument
            callback('Nya');
        }, Math.random() * 2000);
    }

    balas
    0
  • Batalbalas