sử dụng setTimeout trên chuỗi hứa hẹn


115

Ở đây, tôi đang cố gắng xoay quanh những lời hứa. setTimeout trên đó. Nhưng nó cho tôi lỗi JSON sau ( without setTimeout() it works just fine)

SyntaxError: JSON.parse: ký tự không mong muốn ở dòng 1 cột 1 của dữ liệu JSON

tôi muốn biết tại sao nó không thành công?

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
Lưu ý rằng đó returnlà hàm cụ thể và chỉ trả về hàm mẹ, và bạn không thể trả về từ phương thức không đồng bộ.
adeneo

2
Lưu ý rằng có nhiều cách tốt hơn để cấu trúc mã này hơn là sử dụng a globalObj.
Bergi

Ném ở đâu JSON.parse? Tôi cảm thấy khó tin rằng liệu có setTimeoutmột thencuộc gọi lại trong một ảnh hưởng đến cuộc gọi trong lần thengọi lại trước đó hay không .
Bergi

Câu trả lời:


191

Để giữ cho chuỗi lời hứa tiếp tục, bạn không thể sử dụng setTimeout()cách bạn đã làm bởi vì bạn không trả lại lời hứa từ người .then()xử lý - bạn đang trả lại lời hứa từ setTimeout()cuộc gọi lại mà bạn không biết.

Thay vào đó, bạn có thể tạo một hàm trì hoãn nhỏ đơn giản như sau:

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

Và, sau đó sử dụng nó như thế này:

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

Ở đây bạn đang trả lại một lời hứa từ .then()trình xử lý và do đó nó được xâu chuỗi một cách thích hợp.


Bạn cũng có thể thêm một phương thức trì hoãn vào đối tượng Promise và sau đó sử dụng trực tiếp một .delay(x)phương thức trên các hứa hẹn của bạn như sau:

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

Hoặc, sử dụng thư viện lời hứa Bluebird đã có sẵn .delay()phương thức.


1
hàm giải quyết là hàm bên trong then () .. vậy setTimeout (giải quyết, t) có nghĩa là setTimeout (hàm () {return ....}, t) phải không ... vậy tại sao nó sẽ hoạt động?
AL-zami

2
@ AL-zami - delay()trả về một lời hứa sẽ được giải quyết sau setTimeout().
jfriend00 16/09/16

Tôi đã tạo một trình bao bọc lời hứa cho setTimeout để dễ dàng trì hoãn một lời hứa. github.com/zengfenfei/delay
Kevin

4
@pdem - vlà một giá trị tùy chọn mà bạn muốn giải quyết trì hoãn hứa hẹn và do đó chuyển chuỗi hứa hẹn. resolve.bind(null, v)thay cho function() {resolve(v);} Either sẽ hoạt động.
jfriend00

cảm ơn bạn rất nhiều ... độ trễ nguyên mẫu hoạt động nhưng không hoạt động chức năng >>> .then tuyên bố. t là không xác định.
Christian Matthew,

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

CẬP NHẬT:

khi tôi cần ngủ trong chức năng không đồng bộ, tôi sẽ sử dụng

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

Bạn không thể chỉ ngủ trong một chức năng không đồng bộ như vậy? chờ Promise mới (giải quyết => setTimeout (giải quyết, 1000));
Anthony Moon Beam Toorie

@AnthonyMoonBeamToorie đã sửa, ty
Igor Korsakov

Chào mừng bạn của tôi 🧐 hoan hô
Anthony Moon Beam Toorie

52

Phiên bản ES6 ngắn hơn của câu trả lời:

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

Và sau đó bạn có thể làm:

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

và nếu bạn cần rejecttùy chọn, ví dụ như để xác thực eslint, thìconst delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms))
David Thomas

10

Nếu bạn đang ở trong một .Sau đó () khối và bạn muốn thực hiện một 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!!!');

            })

đầu ra sẽ như hình dưới đây

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

Chúc bạn mã hóa vui vẻ!


-1

Trong node.js, bạn cũng có thể làm như sau:

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

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

Tôi đã thử điều này và nhận được số đối số không hợp lệ, dự kiến ​​là 0 trong hàm trì hoãn.
Alex Rindone

Tôi có thể xác nhận rằng nó hoạt động trong node.js 8, 10, 12, 13. Không chắc bạn đang chạy mã của mình như thế nào nhưng tôi chỉ có thể cho rằng utilđang được điền sai. Bạn đang sử dụng một gói hoặc một cái gì đó?
Ngày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.