【问题标题】:Using setTimeout on promise chain在承诺链上使用 setTimeout
【发布时间】:2017-01-25 02:17:00
【问题描述】:

在这里,我试图将我的头绕在 Promise 上。在第一次请求时,我会获取一组链接。在下一次请求时,我会获取第一个链接的内容。但是我想在返回下一个 Promise 对象之前进行延迟。所以我在上面使用 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);
});

【问题讨论】:

  • 请注意,return 是特定于函数的,并且只返回父函数,并且不能从异步方法返回。
  • 请注意有much better ways 来构造此代码而不是使用globalObj
  • JSON.parse 扔哪里了?我很难相信一个then回调中是否有setTimeout会影响上一个then回调中的调用。
  • 这能回答你的问题吗? What is the JavaScript version of sleep()?

标签: javascript json promise settimeout


【解决方案1】:

为了保持 Promise 链继续运行,你不能像以前那样使用 setTimeout(),因为你没有从 .then() 处理程序返回一个 Promise - 你是从 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(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);
});

或者,使用已经内置.delay() 方法的Bluebird promise library

【讨论】:

  • resolve 函数就是 then() 里面的函数。。所以 setTimeout(resolve,t) 的意思是 setTimeout(function(){ return ....},t) 是不是...那么为什么它会起作用呢?
  • @AL-zami - delay() 返回一个将在 setTimeout() 之后解决的承诺。
  • 我为 setTimeout 创建了一个 Promise 包装器,以便轻松地延迟一个 Promise。 github.com/zengfenfei/delay
  • @pdem - v 是一个可选值,您希望延迟承诺解决并因此传递承诺链。 resolve.bind(null, v) 代替 function() {resolve(v);} 都可以。
  • 非常感谢...原型延迟有效,但功能无效>>> .then 语句。 t 未定义。
【解决方案2】:
.then(() => new Promise((resolve) => setTimeout(resolve, 15000)))

更新:

当我需要在异步函数中休眠时,我会抛出

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

【讨论】:

    【解决方案3】:

    答案的较短 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))
    • 看看如何在 Mocha 测试中使用它 - stackoverflow.com/a/70348823/1019307
    【解决方案4】:

    如果您在 .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!!!
    

    编码愉快!

    【讨论】:

      【解决方案5】:

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

      const { promisify } = require('util')
      const delay = promisify(setTimeout)
      
      delay(1000).then(() => console.log('hello'))
      

      【讨论】:

      • 我试过这个并且得到无效的参数数量,在延迟函数中预期为 0。
      • 我可以确认它在 node.js 8、10、12、13 中有效。不确定您是如何运行代码的,但我只能假设 util 被错误地填充。你用的是打包器还是什么的?
      【解决方案6】:

      从node v15开始,你可以使用timers promise API

      文档中的示例:

      import { setTimeout } from 'timers/promises'
      
      const res = await setTimeout(100, 'result')
      
      console.log(res)  // Prints 'result'
      

      【讨论】:

        猜你喜欢
        • 2020-07-20
        • 2020-02-07
        • 2016-04-21
        • 2016-12-09
        • 1970-01-01
        相关资源
        最近更新 更多