menggunakan setTimeout pada rantai perjanjian


115

Di sini saya mencoba untuk membungkus kepala saya di sekitar promise. Di sini, pada permintaan pertama saya mengambil satu set tautan. Dan pada permintaan berikutnya saya mengambil konten tautan pertama. Tetapi saya ingin membuat penundaan sebelum mengembalikan objek promise berikutnya. Jadi saya menggunakan setTimeout padanya Tapi itu memberi saya kesalahan JSON berikut ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: karakter yang tidak diharapkan pada baris 1 kolom 1 dari data JSON

saya ingin tahu mengapa gagal?

let globalObj={};
function getLinks(url){
    return new Promise(function(resolve,reject){

       let http = new XMLHttpRequest();
       http.onreadystatechange = function(){
            if(http.readyState == 4){
              if(http.status == 200){
                resolve(http.response);
              }else{
                reject(new Error());
              }
            }           
       }
       http.open("GET",url,true);
       http.send();
    });
}

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){


    writeToBody(topic);
    setTimeout(function(){
         return getLinks(globalObj["two"]+".txt"); // without setTimeout it works fine 
         },1000);
});

1
Perhatikan bahwa itu returnadalah fungsi khusus, dan hanya mengembalikan ke fungsi induk, dan Anda tidak dapat kembali dari metode asinkron.
adeneo

2
Perhatikan bahwa ada cara yang jauh lebih baik untuk menyusun kode ini daripada menggunakan a globalObj.
Bergi

Dimana JSON.parsemelempar? Saya merasa sulit untuk percaya bahwa apakah ada panggilan balik setTimeoutdalam satu thenmempengaruhi panggilan di panggilan thenbalik sebelumnya .
Bergi

Jawaban:


191

Untuk menjaga agar rantai janji tetap berjalan, Anda tidak dapat menggunakan setTimeout()cara yang Anda lakukan karena Anda tidak mengembalikan janji dari .then()pawang - Anda mengembalikannya dari setTimeout()panggilan balik yang tidak berguna bagi Anda.

Sebagai gantinya, Anda dapat membuat fungsi penundaan kecil sederhana seperti ini:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Dan, kemudian gunakan seperti ini:

getLinks('links.txt').then(function(links){
    let all_links = (JSON.parse(links));
    globalObj=all_links;

    return getLinks(globalObj["one"]+".txt");

}).then(function(topic){
    writeToBody(topic);
    // return a promise here that will be chained to prior promise
    return delay(1000).then(function() {
        return getLinks(globalObj["two"]+".txt");
    });
});

Di sini Anda mengembalikan janji dari .then()pawang dan dengan demikian dirantai dengan tepat.


Anda juga dapat menambahkan metode penundaan ke objek Promise lalu langsung menggunakan .delay(x)metode pada promise Anda seperti ini:

function delay(t, v) {
   return new Promise(function(resolve) { 
       setTimeout(resolve.bind(null, v), t)
   });
}

Promise.prototype.delay = function(t) {
    return this.then(function(v) {
        return delay(t, v);
    });
}


Promise.resolve("hello").delay(500).then(function(v) {
    console.log(v);
});

Atau, gunakan pustaka janji Bluebird yang sudah memiliki .delay()metode bawaan.


1
fungsi menyelesaikan adalah fungsi di dalam then () .. jadi setTimeout (menyelesaikan, t) berarti setTimeout (function () {return ....}, t) bukan ... jadi mengapa itu akan berhasil?
AL-zami

2
@ AL-zami - delay()mengembalikan janji yang akan diselesaikan setelah setTimeout().
jfriend00

Saya telah membuat pembungkus janji untuk setTimeout untuk menunda janji dengan mudah. github.com/zengfenfei/delay
Kevin

4
@pdem - vadalah nilai opsional yang ingin Anda selesaikan dengan janji penundaan dan dengan demikian meneruskan rantai janji. resolve.bind(null, v)berada di tempat function() {resolve(v);} Baik akan bekerja.
jfriend00

terima kasih banyak ... penundaan prototipe berfungsi tetapi tidak fungsi >>>. kemudian pernyataan. t tidak terdefinisi.
Christian Matthew

76
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

MEMPERBARUI:

ketika saya perlu tidur dalam fungsi async saya berikan

await new Promise(resolve => setTimeout(resolve, 1000))

Tidak bisakah Anda tidur dalam fungsi async seperti itu? menunggu Janji baru (menyelesaikan => setTimeout (menyelesaikan, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie diperbaiki, ty
Igor Korsakov

Selamat datang temanku 🧐 cheers
Anthony Moon Beam Toorie

52

Versi ES6 yang lebih pendek dari jawabannya:

const delay = t => new Promise(resolve => setTimeout(resolve, t));

Dan kemudian Anda dapat melakukan:

delay(3000).then(() => console.log('Hello'));

dan jika Anda memerlukan rejectopsi, misalnya untuk validasi eslint, makaconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Jika Anda berada di dalam blok .then () dan Anda ingin mengeksekusi settimeout ()

            .then(() => {
                console.log('wait for 10 seconds . . . . ');
                return new Promise(function(resolve, reject) { 
                    setTimeout(() => {
                        console.log('10 seconds Timer expired!!!');
                        resolve();
                    }, 10000)
                });
            })
            .then(() => {
                console.log('promise resolved!!!');

            })

output akan seperti yang ditunjukkan di bawah ini

wait for 10 seconds . . . .
10 seconds Timer expired!!!
promise resolved!!!

Selamat Coding!


-1

Di node.js Anda juga dapat melakukan hal berikut:

const { promisify } = require('util')
const delay = promisify(setTimeout)

delay(1000).then(() => console.log('hello'))

Saya mencoba ini dan mendapatkan jumlah argumen yang tidak valid, diharapkan 0 dalam fungsi penundaan.
Alex Rindone

Saya dapat mengonfirmasi bahwa ini berfungsi di node.js 8, 10, 12, 13. Tidak yakin bagaimana Anda menjalankan kode Anda, tetapi saya hanya dapat berasumsi bahwa utilsedang di-polyfill secara tidak benar. Apakah Anda menggunakan bundler atau sesuatu?
Jan
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.