代码之家  ›  专栏  ›  技术社区  ›  sigil

当函数本身被调用时,可以在函数中调用对象的方法,但当通过setTimeout调用函数时,对象未定义

  •  -1
  • sigil  · 技术社区  · 6 年前

    我有下面的课 Conference :

    //UrlSerializer is for encoding JSON objects into GET url
    const UrlSerializer=require('./urlSerializer');
    
    class Conference{
    
        constructor(client,workspace){
            this.client=client;
            this.workspace=workspace;
            this.urlSerializer=new UrlSerializer();
        }
    
    
        announce(conferenceSid,timeRemaining){
            var parameters={
                timeRemaining:timeRemaining
            }
            var url=this.urlSerializer.serialize('conferenceAnnounceTime',parameters);
            console.log("conference.announce url: "+url);
            this.client.conferences(conferenceSid)
                .update({
                    announceUrl:url,
                    announceMethod:'GET'
                })
                .then(conference=>console.log(conference.friendlyName));
        }
    
        setTimedAnnounce(initialMinutes,minutesToElapse,conferenceSid){
            var minutesRemaining=initialMinutes-minutesToElapse;
            setTimeout(this.announce,minutesToElapse*60000,minutesRemaining);
        }
    
    }
    
    module.exports=Conference;
    

    UrlSerializer 是:

    const querystring=require('querystring');
    require('env2')('.env');
    
    class UrlSerializer{
    
        constructor(){
            this.paramArrayName="parameters";
        }
    
        serialize(endpoint,paramArray){
            var url=process.env.APP_BASE_URL+"/"+endpoint;
            console.log("urlSerializer base url: "+url);
            var arrayString=JSON.stringify(paramArray);
            console.log("urlSerializer stringified parameter array: "+arrayString);
            var fullUrl=url+"?"+querystring.stringify({[this.paramArrayName]:arrayString});
            console.log("urlSerializer full url: "+fullUrl);
            return fullUrl;
        }   
    }
    
    module.exports=UrlSerializer;
    

    我打电话 conference 来自不同模块中的Express端点的函数 server 如下:

    app.get('/conferenceEvents',function(req,res){
        conferenceSid=req.query.ConferenceSid;
        conference.announce(conferenceSid,initialMinutes);
        conference.setTimedAnnounce(initialMinutes,0.25,conferenceSid);
        res.type('application/json');
        res.status(200).send();
    });
    

    呼唤 conference.announce 控制台输出成功:

    urlSerializer base url: http://x.ngrok.io/conferenceAnnounceTime
    urlSerializer stringified parameter array: {"timeRemaining":5}
    urlSerializer full url: http://x.ngrok.io/conferenceAnnounceTime?parameters=%7B%22timeRemaining%22%3A5%7D
    conference.announce url: http://x.ngrok.io/conferenceAnnounceTime?parameters=%7B%22timeRemaining%22%3A5%7D
    

    但是当通话后15秒 conference.setTimedAnnounce() ,我得到以下错误:

    TypeError: Cannot read property 'serialize' of undefined
        at Timeout.announce [as _onTimeout] (c:\thisAppPath\conference.js:45:30)
        at ontimeout (timers.js:502:15)
        at tryOnTimeout (timers.js:323:5)
        at Timer.listOnTimeout (timers.js:290:5)
    

    我在想也许当计时器打电话的时候 announce 在某种程度上, urlSerializer 实例超出范围。我怎样才能认出它 URL序列化器 什么时候? 宣布 被称为通过 setTimeout() ?

    编辑:我试图保存 this 范围如下:

        setTimedAnnounce(initialMinutes,minutesToElapse,conferenceSid){
            var that=this;
            var minutesRemaining=initialMinutes-minutesToElapse;
            setTimeout(that.announce,minutesToElapse*60000,minutesRemaining);
        }
    

    但这也给了我同样的错误。

    编辑2:

    我用过Barmar的 this.announce.bind(this) 解决方案,解决了 TypeError ,但参数数组和url未在announce;console输出的setTimeout()调用中正确构造,如下所示:

    urlSerializer base url: http://x.ngrok.io/conferenceAnnounceTime
    urlSerializer stringified parameter array: {}
    urlSerializer full url: http://x.ngrok.io/conferenceAnnounceTime?parameters=%7B%7D
    conference.announce url: http://x.ngrok.io/conferenceAnnounceTime?parameters=%7B%7D
    

    我怀疑还有别的地方需要我用 .bind(this) 以便 urlSerializer.serialize() 将被呼叫 在适当的范围内,但我还没有找到。

    编辑3:

    没关系,我知道了-- setTimeout 需要拥有 conferenceSid 作为参数传入 minutesRemaining . 现在开始工作。

    1 回复  |  直到 6 年前
        1
  •  1
  •   Barmar    6 年前
        setTimeout(this.announce,minutesToElapse*60000,minutesRemaining);
    

    需要

        setTimeout(this.announce.bind(this),minutesToElapse*60000,minutesRemaining);
    

    将函数属性作为参数传递不会绑定 this 语境。只有当使用该语法调用方法时,它才会自动绑定。

    推荐文章