ใช้ setTimeout บนสายโซ่สัญญา


115

ที่นี่ฉันพยายามห่อหัวของฉันรอบ ๆ คำสัญญาที่นี่ในการร้องขอครั้งแรกฉันดึงชุดของลิงค์และในการร้องขอครั้งต่อไปฉันดึงเนื้อหาของลิงค์แรก แต่ฉันต้องการชะลอก่อนที่จะส่งคืนวัตถุสัญญาถัดไปดังนั้นฉันจึงใช้ setTimeout แต่มันทำให้ฉันมีข้อผิดพลาด JSON ต่อไปนี้ ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: อักขระที่ไม่คาดคิดที่บรรทัด 1 คอลัมน์ 1 ของข้อมูล JSON

ฉันอยากรู้ว่าทำไมมันถึงล้มเหลว?

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
โปรดทราบว่าreturnเป็นฟังก์ชันเฉพาะและส่งกลับไปยังฟังก์ชันหลักเท่านั้นและคุณไม่สามารถส่งคืนจากเมธอด async ได้
adeneo

2
สังเกตว่ามีวิธีที่ดีกว่าในการจัดโครงสร้างโค้ดนี้มากกว่าการใช้ไฟล์globalObj.
Bergi

สถานที่ที่ไม่JSON.parseโยน? ฉันคิดว่ามันยากที่จะเชื่อว่ามีการโทรกลับsetTimeoutในครั้งเดียวthenส่งผลต่อการโทรในการthenโทรกลับครั้งก่อนหรือไม่
Bergi

คำตอบ:


191

เพื่อให้ห่วงโซ่สัญญาดำเนินต่อไปคุณไม่สามารถใช้setTimeout()วิธีที่คุณทำได้เนื่องจากคุณไม่ได้คืนสัญญาจาก.then()ตัวจัดการ - คุณกำลังส่งคืนจากการsetTimeout()ติดต่อกลับซึ่งคุณไม่ได้ผลดี

คุณสามารถสร้างฟังก์ชันหน่วงเวลาเล็กน้อยเช่นนี้แทนได้:

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

จากนั้นใช้มันดังนี้:

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");
    });
});

ที่นี่คุณกำลังคืนคำสัญญาจาก.then()ผู้จัดการและถูกผูกมัดอย่างเหมาะสม


คุณยังสามารถเพิ่มวิธีการหน่วงเวลาให้กับวัตถุ Promise จากนั้นใช้.delay(x)วิธีการกับคำสัญญาของคุณโดยตรงดังนี้:

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);
});

หรือใช้ไลบรารีสัญญาของ Bluebirdซึ่งมี.delay()วิธีการในตัวอยู่แล้ว


1
ฟังก์ชันแก้ไขคือฟังก์ชันที่อยู่ข้างในแล้ว () .. ดังนั้น setTimeout (แก้ไข, t) หมายถึง setTimeout (ฟังก์ชัน () {return .... }, t) ไม่ใช่ ... แล้วทำไมมันถึงใช้งานได้?
AL-zami

2
@ AL-zami - delay()ส่งคืนสัญญาที่จะได้รับการแก้ไขหลังจากsetTimeout().
jfriend00

ฉันได้สร้างตัวห่อสัญญาสำหรับ setTimeout เพื่อให้สัญญาล่าช้าได้อย่างง่ายดาย github.com/zengfenfei/delay
Kevin

4
@pdem - vเป็นค่าทางเลือกที่คุณต้องการให้สัญญาล่าช้าแก้ไขและส่งต่อห่วงโซ่สัญญา resolve.bind(null, v)แทนfunction() {resolve(v);} ทั้งสองอย่างจะทำงาน
jfriend00

ขอบคุณมาก ... ความล่าช้าของต้นแบบใช้งานได้ แต่ไม่ใช่ฟังก์ชัน >>> จากนั้นคำสั่ง t ไม่ได้กำหนด
Christian Matthew

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

UPDATE:

เมื่อฉันต้องการการนอนหลับในฟังก์ชัน async ฉันจะโยนเข้าไป

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

คุณไม่สามารถนอนหลับภายในฟังก์ชัน async ได้หรือไม่? รอสัญญาใหม่ (แก้ไข => setTimeout (แก้ไข 1,000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie fixed, ty
Igor Korsakov

ยินดีต้อนรับเพื่อนของฉัน🧐เชียร์
Anthony Moon Beam Toorie

52

คำตอบเวอร์ชัน ES6 ที่สั้นกว่า:

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

จากนั้นคุณสามารถทำได้:

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

และถ้าคุณต้องการrejectตัวเลือกเช่นสำหรับการตรวจสอบความถูกต้องของ eslint ดังนั้นconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

หากคุณอยู่ในบล็อก. then ()และคุณต้องการเรียกใช้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!!!');

            })

ผลลัพธ์จะดังที่แสดงด้านล่าง

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

Happy Coding!


-1

ใน node.js คุณสามารถทำสิ่งต่อไปนี้ได้:

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

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

ฉันลองสิ่งนี้และได้รับจำนวนอาร์กิวเมนต์ที่ไม่ถูกต้องคาดว่าจะเป็น 0 ภายในฟังก์ชันการหน่วงเวลา
Alex Rindone

ฉันสามารถยืนยันได้ว่ามันใช้งานได้ใน node.js 8, 10, 12, 13 ไม่แน่ใจว่าคุณกำลังเรียกใช้โค้ดของคุณอย่างไร แต่ฉันสามารถสันนิษฐานได้ว่าutilมีการเติม polyfilled อย่างไม่ถูกต้อง คุณใช้บันเดิลเลอร์หรืออะไร?
ม.ค.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.