虽然未定义变量 - 等待
Posted
技术标签:
【中文标题】虽然未定义变量 - 等待【英文标题】:While variable is not defined - wait 【发布时间】:2011-11-10 14:25:21 【问题描述】:我有一个click
事件第一次从另一个地方自动触发。我的问题是它运行得太快了,因为所需的变量仍在由 Flash 和 Web 服务定义。所以现在我有:
(function ($)
$(window).load(function()
setTimeout(function()
$('a.play').trigger("click");
, 5000);
);
)(jQuery);
问题在于,对于互联网连接速度较慢的人来说,5 秒可能太快,反之亦然,对于互联网连接速度较快的人来说,它太慢了。
那么在定义someVariable
之前,我应该如何处理延迟或超时?
【问题讨论】:
没有可以响应的“就绪”事件吗? 嗯,没有通用的方法来告诉“等到某个变量被定义”(在愚蠢的实现中,只要定义了变量,它就会每 X 毫秒检查一次)。但是,如果您等待的变量是由您的(我的意思是由您管理的)代码设置的,那么您可以定义一个命名函数(回调),该函数将在所需的变量定义之后立即调用。 jQuery 中应该存在 watch - 或类似的方法。 【参考方案1】:我建议使用@dnuttle 的答案改编。
使用 try-catch 块的优点是,如果您尝试执行的代码的任何部分失败,则整个块都会失败。我觉得这很有用,因为它给了你一种交易;一切都完成了。
你不应该编写可能由于外部因素而导致无限循环的代码。如果你正在等待来自 ajax 请求的响应而服务器没有响应,这正是会发生的情况。不回应。我认为对任何有问题的循环设置超时是一个好习惯。
let time = 0; // Used to keep track of how long the loop runs
function waitForElement()
try
// I'm testing for an element, but this condition can be
// any reasonable condition
if (document.getElementById('test') === null)
throw 'error';
// This is where you can do something with your variable
// document.getElementById('test').addEventListener....
// or call a function that uses your value
catch (error)
// Loop stops executing if not successful within about 5 seconds
if (time > 5000)
// I return false on failure so I can easily check for failure
return false;
else
// Increment the time and call the function again
time += 250;
setTimeout(waitForElement, 250);
// Call the function after the definition, ensures that time is set
waitForElement();
【讨论】:
【参考方案2】:while (typeof myVar == void(0))
if ( typeof myVar != void(0))
console.log(myVar);
这使用了 typeof 运算符,如果变量尚未声明,则该运算符仅返回 undefined。应该适用于所有类型的 javascript。
【讨论】:
typeof
总是返回一个字符串,void 0
总是返回文字值undefined
,它们永远不会等价。此外,void 0
是 tersers/uglifiers 中用于减少字符数的技巧;你不应该自己写出来。最后,这段代码是同步的,所以它会阻塞主线程直到它完成。此线程中已经有多种其他正确方法,请改用其中一种。【参考方案3】:
async, await
实现,对@Toprak's answer 的改进
(async() =>
console.log("waiting for variable");
while(!window.hasOwnProperty("myVar")) // define the condition as you like
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("variable is defined");
)();
console.log("above code doesn't block main function stack");
在重新审视 OP 的问题之后。实际上有一种更好的方法来实现预期的目标:“变量集回调”。虽然下面的代码仅在所需变量由对象(或窗口)封装而不是由let
或var
声明时才有效(我留下了第一个答案,因为我只是对现有答案进行改进而没有实际阅读原始答案问题):
let obj = encapsulatedObject || window;
Object.defineProperty(obj, "myVar",
configurable: true,
set(v)
Object.defineProperty(obj, "myVar",
configurable: true, enumerable: true, writable: true, value: v );
console.log("window.myVar is defined");
);
见Object.defineProperty 或使用es6 proxy(这可能是矫枉过正)
如果您正在寻找更多:
/**
* combining the two as suggested by @Emmanuel Mahuni,
* and showing an alternative to handle defineProperty setter and getter
*/
let obj = || window;
(async() =>
let _foo = await new Promise(res =>
Object.defineProperty(obj, "foo", set: res );
);
console.log("obj.foo is defined with value:", _foo);
)();
/*
IMPORTANT: note that obj.foo is still undefined
the reason is out of scope of this question/answer
take a research of Object.defineProperty to see more
*/
// TEST CODE
console.log("test start");
setTimeout(async () =>
console.log("about to assign obj.foo");
obj.foo = "Hello World!";
// try uncomment the following line and compare the output
// await new Promise(res => setTimeout(res));
console.log("finished assigning obj.foo");
console.log("value of obj.foo:", obj.foo); // undefined
// console: obj.foo is defined with value: Hello World!
, 2000);
【讨论】:
对我来说,async
版本的优势在于您可以调整它以在运行代码之前等待多个变量或其他条件。
如果你将它与上面的异步策略结合起来,你就可以做到这一点,事情是 defineProperty 策略不涉及轮询,这是一个巨大的优势。我已经在使用异步方式,并且想要一种更接近 js 引擎的方式来实现它,这很成功。
@JRI 你可以用Object.defineProperty
让你等待多个变量如果所有变量都封装在某个对象中,只需设置一个局部变量count = <number of variables>
,-1
每次并运行当计数达到0
时回调,它的好处是它是瞬时的。我最近遇到了类似的问题(等待多次加载),您可以编写帮助函数来减少样板。【参考方案4】:
参加聚会很晚了,但我想为任何关注这个问题的未来开发人员提供更现代的解决方案。它基于 Toprak 的回答,但经过简化以更清楚地了解正在发生的事情。
<div>Result: <span id="result"></span></div>
<script>
var output = null;
// Define an asynchronous function which will not block where it is called.
async function test()
// Create a promise with the await operator which instructs the async function to wait for the promise to complete.
await new Promise(function(resolve, reject)
// Execute the code that needs to be completed.
// In this case it is a timeout that takes 2 seconds before returning a result.
setTimeout(function()
// Just call resolve() with the result wherever the code completes.
resolve("success output");
, 2000);
// Just for reference, an 'error' has been included.
// It has a chance to occur before resolve() is called in this case, but normally it would only be used when your code fails.
setTimeout(function()
// Use reject() if your code isn't successful.
reject("error output");
, Math.random() * 4000);
)
.then(function(result)
// The result variable comes from the first argument of resolve().
output = result;
)
.catch(function(error)
// The error variable comes from the first argument of reject().
// Catch will also catch any unexpected errors that occur during execution.
// In this case, the output variable will be set to either of those results.
if (error) output = error;
);
// Set the content of the result span to output after the promise returns.
document.querySelector("#result").innerhtml = output;
// Execute the test async function.
test();
// Executes immediately after test is called.
document.querySelector("#result").innerHTML = "nothing yet";
</script>
为了便于视觉理解,这里是没有 cmets 的代码。
var output = null;
async function test()
await new Promise(function(resolve, reject)
setTimeout(function()
resolve("success output");
, 2000);
setTimeout(function()
reject("error output");
, Math.random() * 4000);
)
.then(function(result)
output = result;
)
.catch(function(error)
if (error) output = error;
);
document.querySelector("#result").innerHTML = output;
test();
document.querySelector("#result").innerHTML = "nothing yet";
最后一点,according to MDN,所有现代浏览器都支持 Promise,唯一的例外是 Internet Explorer。此兼容性信息也是supported by caniuse。然而,随着 Bootstrap 5 放弃对 Internet Explorer 的支持,以及基于 webkit 的新 Edge,这对大多数开发人员来说不太可能成为问题。
【讨论】:
【参考方案5】:我更喜欢这样简单的东西:
function waitFor(variable, callback)
var interval = setInterval(function()
if (window[variable])
clearInterval(interval);
callback();
, 200);
然后将它与someVariable
的示例变量一起使用:
waitFor('someVariable', function()
// do something here now that someVariable is defined
);
请注意,您可以进行各种调整。在上面的setInterval
调用中,我传递了200
作为间隔函数应该运行的频率。在检查变量之前还有一段固有的延迟时间(~200 毫秒)——在某些情况下,最好立即检查它,这样就不会出现延迟。
【讨论】:
【参考方案6】:Object.defineProperty(window, 'propertyName',
set: value =>
this._value = value;
// someAction();
,
get: () => this._value
);
或者即使您只是希望将此属性作为参数传递给函数并且不需要在全局对象上定义它:
Object.defineProperty(window, 'propertyName', set: value => someAction(value) )
但是,由于在您的示例中,您似乎希望在创建节点时执行操作,我建议您查看MutationObservers。
【讨论】:
【参考方案7】:我对@dnuttle 的answer 投了赞成票,但最终使用了以下策略:
// On doc ready for modern browsers
document.addEventListener('DOMContentLoaded', (e) =>
// Scope all logic related to what you want to achieve by using a function
const waitForMyFunction = () =>
// Use a timeout id to identify your process and purge it when it's no longer needed
let timeoutID;
// Check if your function is defined, in this case by checking its type
if (typeof myFunction === 'function')
// We no longer need to wait, purge the timeout id
window.clearTimeout(timeoutID);
// 'myFunction' is defined, invoke it with parameters, if any
myFunction('param1', 'param2');
else
// 'myFunction' is undefined, try again in 0.25 secs
timeoutID = window.setTimeout(waitForMyFunction, 250);
;
// Initialize
waitForMyFunction();
);
它已经过测试并且可以正常工作! ;)
要点:https://gist.github.com/dreamyguy/f319f0b2bffb1f812cf8b7cae4abb47c
【讨论】:
【参考方案8】:使用 Ecma Script 2017 您可以使用 async-await 和 while 一起执行此操作 虽然不会崩溃或锁定程序,即使变量永远不会为真
//First define some delay function which is called from async function
function __delay__(timer)
return new Promise(resolve =>
timer = timer || 2000;
setTimeout(function ()
resolve();
, timer);
);
;
//Then Declare Some Variable Global or In Scope
//Depends on you
let Variable = false;
//And define what ever you want with async fuction
async function some()
while (!Variable)
await __delay__(1000);
//...code here because when Variable = true this function will
;
////////////////////////////////////////////////////////////
//In Your Case
//1.Define Global Variable For Check Statement
//2.Convert function to async like below
var isContinue = false;
setTimeout(async function ()
//STOPT THE FUNCTION UNTIL CONDITION IS CORRECT
while (!isContinue)
await __delay__(1000);
//WHEN CONDITION IS CORRECT THEN TRIGGER WILL CLICKED
$('a.play').trigger("click");
, 1);
/////////////////////////////////////////////////////////////
此外,在这种情况下,您不必使用 setTimeout,只需将准备好的函数设为异步...
【讨论】:
setTimeout()
部分中的一些乏味代码,无法完成:setTimeout(resolve, timer)
in __delay__
记住 resolve
本身是可调用的,setTimeout()
只需要第一个参数的可调用,不必再包装一次resolve
。完整代码:function delay(timer=2000) return new Promise(resolve => setTimeout(resolve, timer));
clean.
as if in my answer
你的延迟方法的Typescript简化版:export const usleep = (ms=1000) => new Promise我更喜欢这段代码:
function checkVariable()
if (variableLoaded == true)
// Here is your next action
setTimeout(checkVariable, 1000);
【讨论】:
我将它与typeof someVariable !== 'undefined'
结合使用,而且我还必须使用这里的信息:***.com/questions/1055767/…
这不是一个好的解决方案,因为任何进程或 dom 的加载可能需要超过 1000 毫秒,然后这会中断流程。【参考方案10】:
您可以在完成后让 Flash 调用该函数。我不确定您所说的网络服务是什么意思。我假设您有 JavaScript 代码通过 Ajax 调用 Web 服务,在这种情况下您会知道它们何时终止。在最坏的情况下,您可以执行循环 setTimeout
,每隔 100 毫秒左右检查一次。
并且检查是否定义了变量可以只是if (myVariable)
或更安全:if(typeof myVariable == "undefined")
【讨论】:
【参考方案11】:以下将继续寻找 someVariable 直到找到它。它每 0.25 秒检查一次。
function waitForElement()
if(typeof someVariable !== "undefined")
//variable exists, do what you want
else
setTimeout(waitForElement, 250);
【讨论】:
我应该添加建议“准备好”可能更好的评论;我的解决方案可以使用或不使用 jQuery。 不需要额外的功能。做setTimeout(waitForElement,250);
setInterval()
似乎更适合这里
setInterval() 是错误的。一旦定义了 someVariable,它就不需要继续调用该函数了。
我知道这是旧的,但我今天偶然发现了这个。包装函数不是多余的。它围绕代码创建一个闭包,创建一个保持其私有的模块。这是一种很好的做法,可以防止全局窗口被不必要的代码堵塞。【参考方案12】:
这是一个示例,其中等待设置变量的所有逻辑都被推迟到一个函数,然后调用一个回调来执行程序需要执行的所有其他操作 - 如果您需要在执行其他任何操作之前加载变量,这感觉就像一种简洁的方式,因此您将变量加载与其他所有内容分开,同时仍然确保“其他所有内容”本质上是一个回调。
var loadUser = function(everythingElse)
var interval = setInterval(function()
if(typeof CurrentUser.name !== 'undefined')
$scope.username = CurrentUser.name;
clearInterval(interval);
everythingElse();
,1);
;
loadUser(function()
//everything else
);
【讨论】:
【参考方案13】:更短的方式:
var queue = function (args)
typeof variableToCheck !== "undefined"? doSomething(args) : setTimeout(function () queue(args), 2000);
;
你也可以传递参数
【讨论】:
【参考方案14】:你可以用这个:
var refreshIntervalId = null;
refreshIntervalId = setInterval(checkIfVariableIsSet, 1000);
var checkIfVariableIsSet = function()
if(typeof someVariable !== 'undefined')
$('a.play').trigger("click");
clearInterval(refreshIntervalId);
;
【讨论】:
【参考方案15】:不要使用windows加载事件,而是使用文档上的ready事件。
$(document).ready(function()[...]);
当 DOM 中的所有内容都准备就绪时,这应该会触发,包括完全加载的媒体内容。
【讨论】:
这将起作用,除非 someVariable 的存在是 ajax 调用的结果。 Ajax 调用可能(可能会)在调用就绪后完成。 不,该变量仅在一段时间后才存在。我使用(window).load
,因为我在这里看到一个帖子,它正在加载之后 (document).ready
。在这种情况下,后者对我来说更好。
是的。鉴于这种情况,他可以在 ajax 请求中从他的成功处理程序中触发 JS 事件。 Afaik 这可以从 Flash 到 JS 完成。
如果相关脚本和定义变量的脚本都使用 $(document).ready 并且定义变量的脚本包含在使用它的脚本之后,则此方法不起作用。跨度>
在 DOM 下载后触发 Document ready,而不是媒体。如果您需要等待媒体下载,请使用窗口加载。以上是关于虽然未定义变量 - 等待的主要内容,如果未能解决你的问题,请参考以下文章