如何在 JavaScript 中实现异步函数之间的依赖关系
How to implement dependency between asynchronous functions in JavaScript?
作为一个简化的情况,我有两个异步函数,foo
和bar
。 bar
需要foo
的结果,即 bar
取决于foo
.我不知道将首先调用哪个函数。
- 如果首先调用
bar
,bar
将调用foo
并在foo
完成后立即启动自身。 - 如果首先调用并完成
foo
,则bar
可以使用foo
的结果。 - 如果先调用
foo
,bar
在foo
完成之前调用,bar
需要等待foo
的结果。(不要调用新的调用foo
,只需等待已经触发的调用foo
(
我怎样才能做到这一点?
是否可以注册一个异步函数依赖链(类似于require.js define['foo'], function() { bar(); }
中的依赖关系(?
我可以使用$.deferred()
来实现它吗?
如何?
在这种情况下,标准方法是缓存较低级别的承诺。
通常,您将在某个合适的外部作用域中建立一个 js 纯对象作为 promise 缓存,并且在调用异步进程之前始终先查看那里。
var promiseCache = {};
function foo() {
if(!promiseCache.foo) {
promiseCache.foo = doSomethingAsync();
}
return promiseCache.foo;
}
function bar() {
return foo().then(doSomethingElseAsync);
}
当然,如果合适,没有什么可以阻止您缓存更高级别的承诺。
function bar() {
if(!promiseCache.bar) {
promiseCache.bar = foo().then(doSomethingElseAsync);
}
return promiseCache.bar;
}
编辑:forceRefresh
功能
您可以通过传递(额外(参数来强制函数刷新其缓存的承诺。
function foo(any, number, of, other, arguments, forceRefresh) {
if(forceRefresh || !promiseCache.foo) {
promiseCache.foo = doSomethingAsync();
}
return promiseCache.foo;
}
通过forceRefresh
最后一个参数,将其省略与传递false
相同,并且foo
将使用缓存的承诺(如果可用(。或者,传递true
以确保调用doSomethingAsync()
并刷新缓存的值。
编辑 2: setName((/getName((
在getName()
forceRefresh
机制到位的情况下:
setName(newName).then(getName.bind(null, true)); //set new name then read it back using forceRefresh.
或者,省略forceRefresh
机制,并假设缓存属性promiseCache.name
:
setName(newName).then(function() {
promiseCache.name = $.when(newName);//update the cache with a simulated `getName()` promise.
});
第一种方法更优雅,第二种方法更有效。
您可以简单地将这两个函数视为独立的。这样,您就不会以菊花链方式连接异步运行的依赖项。然后,您可以拥有另一个使用它们的模块。
由于他们做异步的事情,请考虑使用承诺。你可以使用 jQuery 的延迟来实现兼容性。将延迟视为读/写,而承诺是只读的。
// foo.js
define(function(){
return function(){
return new Promise(function(resolve, reject){
// Do async stuff. Call resolve/reject accordingly
});
};
});
// bar.js
define(function(){
return function(){
return new Promise(function(resolve, reject){
// Do async stuff. Call resolve/reject accordingly
});
};
});
// Your code (Excuse the CommonJS format. Personal preference)
define(function(require){
// Require both functions
var foo = require('foo');
var bar = require('bar');
// Use them
foo(...).then(function(response){
return bar();
}).then(function(){
// all done
});;
});
尝试使用可能的值创建对象属性 undefined
、 "pending"
、 true
;当obj.active
true
时调用 deferred.resolve()
,deferred.reject()
当obj.active
"挂起"时调用
var res = {
active: void 0
};
var foo = function foo(state) {
var t;
var deferred = function(type) {
return $.Deferred(function(dfd) {
if (res.active === "pending" || state && state === "pending") {
res.active = "pending";
dfd.rejectWith(res, [res.active])
} else {
res.active = state || "pending";
t = setInterval(function() {
console.log(res.active)
}, 100);
setTimeout(function() {
clearInterval(t)
res.active = true;
dfd.resolveWith(res, [res.active])
}, 3000);
}
return dfd.promise()
})
.then(function(state) {
console.log("foo value", state);
return state
}, function(err) {
console.log("foo status", err)
return err
})
}
return deferred()
}
var bar = function bar(result) {
var deferred = function(type) {
return $.Deferred(function(dfd) {
if (result && result === true) {
setTimeout(function() {
dfd.resolveWith(result, [true])
}, 1500)
} else {
dfd.rejectWith(res, [res.active || "pending"])
};
return dfd.promise()
})
}
return deferred().then(function(data) {
console.log("bar value", data);
}, function(err) {
console.log("bar status", err);
})
}
$("button").click(function() {
$(this).is(":first")
? foo().then(bar, bar)
: bar(res.active === true ? res.active : "pending")
.then(foo, foo).then(bar, bar)
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button>foo</button>
<button>bar</button>
不确定我是否正确理解了这个问题。但这是我的看法:
-
将函数 foo 放入变量中
var foo_fn = function foo(foo_args){// Your async code goes here}
-
Foo 是异步的,并在某个时候返回一些东西。在你对foo的定义中,我建议你使用promises,这个概念旨在以干净和可扩展的方式管理异步函数的组合。jQuery概念的实现在许多简单的用例中都很方便,但存在一些缺点,这使得您在某个时候使用遵循Promises/A规范的众多Promise库之一变得有趣。有关更多信息,您可以参考:参看 https://thewayofcode.wordpress.com/2013/01/22/javascript-promises-and-why-jquery-implementation-is-broken/和 https://blog.domenic.me/youre-missing-the-point-of-promises
-
因此,假设 Foo 接受 args,并返回一个承诺,该承诺稍后解析为某个值。
var foo_fn = function foo(foo_args) { return foo_fn.promise = new RSVP.Promise (resolve, reject) {
// Your async code goes here } }在这里,我使用 RSVP 承诺库,但任何遵循 Promises/A 规范的承诺库都可以完成这项工作。
-
当调用 bar 时,您可以执行以下操作:
function bar (bar_args) { var foo_promise = foo_fn.promise; // if foo was called, whether the computation is in progress or finished, // the foo_fn.promise field will be non-empty, as foo returns immediately // with a promise anytime it is called `` if (!foo.promise) { // foo has not yet been called so call it foo_promise = foo(foo_args); } foo_promise.then (function (foo_result) {/*some async code here*/}) }
注意:该解决方案与Roamer-1888
提出的解决方案非常相似。一个区别是,在 Roamer 提案中,foo
函数在执行一次异步计算后将始终返回相同的值。不知道这是否是预期的行为。在我的实现中,foo执行异步。每次调用时计算。 bar
将使用存储在字段 foo_fn.promise
中的最新计算值。较旧的计算将丢失,不考虑正在进行的可能计算。
如果你打算在代码中经常使用此模式,你也可以创建一个处理define
模型的函数 功能在require.js
.
您将需要 :
-
用于保存依赖项函数的注册表(示例中
foo
( -
依赖函数(在您的示例中
bar
(将需要接受依赖函数计算值作为其签名的一部分。例如,依赖项的哈希值可以作为第一个参数传递,因此柱签名可以是:{foo: foo_result}, other_bar_args...
-
依赖函数必须遵循我之前答案的模型,即在执行时将它们的承诺值注册为自身的属性。
-
提醒 :您需要命名这些依赖项函数以在其正文中引用它们,然后将该对象添加到注册表中。
在define
函数体中,您将依赖函数包装到另一个函数中,该函数:
-
从注册表中获取所有依赖项
-
获取所有依赖项值,必要时执行依赖项(类似于我之前的答案(。这意味着您最终会得到一个承诺列表,然后将其结果聚集在一起(例如
RSVP.hash
RSVP 承诺库(。我相信jQuery与jQuery.when
有类似的功能 -
你调用依赖函数(
bar
(作为第一个参数,这个结果哈希作为第一个参数,其他参数与包装的函数相同 -
该包装函数是新的
bar
,因此当调用bar
时,将被调用的是包装的函数。
有点长,但它应该可以工作。如果您想查看一些代码,请告诉我这是否是您正在寻找的。无论如何,如果您要进行复杂的异步。在您的代码中,使用兼容的 promise 库可能会很有趣。$.deferred也只有在你没有更好的东西时才使用,因为它使你更难跟踪函数的行为:你需要跟踪所有延迟似乎能够推理你的程序的地方。
- 函数参数中的数据与指定变量之间的任何性能差异
- JavaScript中的函数和对象之间没有区别吗?
- Jquery在函数之间传递表行
- d3中堆栈函数和嵌套函数之间的差异
- javascript函数的:和=之间的区别
- 函数中this和var之间的区别
- 如何在函数之间切换
- JavaScript/jQuery-添加添加和删除类与下一个函数之间的延迟
- Javascript-defineProperty和直接在对象上定义函数之间的区别
- 函数声明与函数表达式之间的性能差异
- 函数()和新函数()之间的区别
- MeteorJS-函数()和()之间的差异=>
- JavaScript函数,用于计算两个日期之间的年、月和天数
- 在Jquery调用之间添加其他函数
- 在函数之间传递javascript变量
- 两个函数之间的角度承诺
- 命名一个在“”和“”之间切换元素的函数;启用”;以及“;被禁用”;州
- 如何在异步函数与Deferredjquery之间同步
- 如何创建一个确定2个数字之间值的函数
- Javascript创建函数,以便在其他函数之间共享变量