在承诺链上使用setTimeout


115

在这里,我试图绕过promises。在第一个请求时,我获取了一组链接。在下一个请求时,我获取了第一个链接的内容。但是我想在返回下一个promise对象之前进行延迟。所以我使用setTimeout就可以了,但是它给了我下面的JSON错误(without setTimeout() it works just fine

SyntaxError:JSON.parse:JSON数据的第1行第1列出现意外字符

我想知道为什么会失败?

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是特定于函数的,并且仅返回到父函数,并且您不能从异步方法返回。
adeneo

2
请注意,与使用相比,有更好的方法来构造此代码globalObj
Bergi

JSON.parse扔在哪里?我很难相信setTimeout一个then回调中是否存在会影响上一个回调中的then调用。
Bergi

Answers:


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 ,因此它被适当地链接了。


您还可以向Promise对象添加一个delay方法,然后.delay(x)对您的promise 直接使用一个方法,如下所示:

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 Promise库.delay()


1
resolve function是then().. so中的函数,因此setTimeout(resolve,t)意味着setTimeout(function(){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

非常感谢...原型延迟有效,但功能>>> .then语句无效。t是不确定的。
基督教马修

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

更新:

当我需要在异步功能中睡眠时,我会抛出

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

您不能像这样在异步函数中入睡吗?等待新的Promise(resolve => setTimeout(resolve,1000));
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!!!

编码愉快!


-1

在node.js中,您还可以执行以下操作:

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

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

我尝试了此操作,并得到了无效数量的参数,在delay函数中预期为0。
Alex Rindone

我可以确认它可以在node.js 8、10、12、13中正常工作。不确定您如何运行代码,但我只能假设util错误地进行了Polyfill。您在使用捆扎机之类的东西吗?
1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.