gpt4 book ai didi

javascript - Kyle Simpson asyncify 函数来自你不知道的 JS : Async & Performance

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:45:01 25 4
gpt4 key购买 nike

如果有人能详细解释一下这个函数是干什么的?这部分在做什么: fn = orig_fn.bind.apply(orig_fn,

谢谢。

function asyncify(fn) {
var orig_fn = fn,
intv = setTimeout( function(){
intv = null;
if (fn) fn();
}, 0 )
;

fn = null;

return function() {
// firing too quickly, before `intv` timer has fired to
// indicate async turn has passed?
if (intv) {
fn = orig_fn.bind.apply(
orig_fn,
// add the wrapper's `this` to the `bind(..)`
// call parameters, as well as currying any
// passed in parameters
[this].concat( [].slice.call( arguments ) )
);
}
// already async
else {
// invoke original function
orig_fn.apply( this, arguments );
}
};
}

最佳答案

代码似乎是一种非常复杂的表达方式:

function asyncify(cb) {
return function() {
setTimeout(function() {
cb.apply(this, arguments);
}, 0);
}
}

但我应该强调“似乎”。也许我在上面的来回中遗漏了一些重要的细微差别。

至于bind.apply,解释起来有点棘手。两者都是每个函数的方法,允许您使用指定的上下文 (this) 调用它,在 apply 的情况下,它接受参数作为数组。

当我们“应用”bind 时,bind 本身 是正在应用的函数——而不是 apply 存在的对象,它可以是任何东西。因此,如果我们像这样重写它,可能更容易开始理解这一行:

Function.prototype.bind.apply(...)

Bind 有这样的签名:.bind(context, arg1, arg2...)

参数是可选的——它们通常用于柯里化(Currying),这是 bind 的主要用例之一。在这种情况下,作者希望将原始函数绑定(bind)到 (1) 当前 this 上下文,(2) 调用“asyncified”函数的参数。因为我们事先不知道需要传递多少参数,所以我们必须使用 apply,其中参数可以是数组或实际的 arguments 对象。这是对本节的非常冗长的重写,可能有助于阐明发生的情况:

var contextOfApply = orig_fn;
var contextWithWhichToCallOriginalFn = this;

var argumentArray = Array.prototype.slice.call(arguments);

argumentArray.unshift(contextWithWhichToCallOriginalFn);

// Now argument array looks like [ this, arg1, arg2... ]
// The 'this' is the context argument for bind, and therefore the
// context with which the function will end up being called.

fn = Function.prototype.bind.apply(contextOfApply, argumentArray);

其实……

我可以解释我提供的简单版本有何不同。再次查看它时,我发现了缺少的细微差别,正是这种细微差别导致其作者在顶部经历了那场奇怪的舞蹈。它实际上不是使另一个函数“始终异步”的函数。这是一个仅确保它是异步 一次 的函数——它防止在创建回调的同一时刻执行回调,但此后它会同步执行。

不过,我认为还是可以用更友好的方式来写:

function asyncify(cb) {
var inInitialTick = true;

setTimeout(function() { inInitialTick = false; }, 0);

return function() {
var self = this;
var args = arguments;

if (inInitialTick)
setTimeout(function() { cb.apply(self, args); }, 0);
else
cb.apply(self, args);
}
}

现在我应该注意到上面的内容实际上并没有按照它说的去做。事实上,函数使用超时与同步执行的次数是随机的,无论是使用这个版本还是原始版本。这是因为 setTimeout 是 setImmediate 的一个蹩脚的(但有时很好)替代品,这显然是这个函数真正想要的(但如果它需要在 Moz 和 Chrome 中运行,它可能无法拥有)。

这是因为传递给 setTimeout 的毫秒值是一种“软目标”。它实际上不会为零;事实上,如果我没记错的话,它总是至少 4 毫秒,这意味着任何数量的滴答声都可能通过。

想象一下,你在一个神奇的仙境,ES6 的东西在那里工作,并且没有奇怪的手绞痛是否要实现像 setImmediate 这样基本的实用程序,它可以像这样重写,然后它会是可预测的行为,因为与设置为 0 的 setTimeout 不同,setImmediate 确实确保执行发生在下一个滴答声而不是稍后的滴答声:

const asyncify = cb => {
var inInitialTick = true;

setImmediate(() => inInitialTick = false);

return function() {
if (inInitialTick)
setImmediate(() => cb.apply(this, arguments));
else
cb.apply(this, arguments);
}
};

实际上实际上...

还有一个区别。在原来的情况下,如果在“当前滴答,实际上是任意数量的连续滴答”期间调用,它仍然只会执行一次初始时间,并使用最后一组参数。这实际上闻起来有点像它可能是无意的行为,但没有上下文我只是猜测;这可能正是我们的意图。这是因为在第一次超时完成之前的每次调用中, fn 都会被覆盖。这种行为有时被称为节流,但在这种情况下,与“正常”节流不同,它只会在创建后 4 毫秒左右的未知时间长度内发生,此后将不受节流和同步。祝那些必须调试由此调用的 Zalgo 的人好运 :)

关于javascript - Kyle Simpson asyncify 函数来自你不知道的 JS : Async & Performance,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30383542/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com