看看这个 code:
<a href="#" id="link">Link</a>
<span>Moving</span>
$('#link').click(function () {
console.log("Enter");
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
});
console.log("Exit");
});
正如您在控制台中看到的,“动画”函数是异步的,它“分叉”了事件处理程序块代码的流程。实际上 :
$('#link').click(function () {
console.log("Enter");
asyncFunct();
console.log("Exit");
});
function asyncFunct() {
console.log("finished");
}
按照块代码的流程!
如果我希望使用这种行为创建我的 function asyncFunct() { }
,我该如何使用 javascript/jquery 来实现?我认为有一个不使用setTimeout()
的策略
您不能制作真正自定义的异步函数。您最终将不得不利用本机提供的技术,例如:
设置间隔
设置超时
requestAnimationFrame
XMLHttpRequest
网络套接字
工人
一些 HTML5 API,例如 File API、Web Database API
支持 onload 的技术
... 好多其它的
事实上,对于动画 jQuery uses setInterval
。
您可以使用计时器:
setTimeout( yourFn, 0 );
(其中 yourFn
是对您的函数的引用)
或者,使用 Lodash:
_.defer( yourFn );
推迟调用 func 直到当前调用堆栈被清除。调用 func 时会提供任何其他参数。
setTimeout
的最短时间为 4 毫秒。给它 0 仍然需要最少的时间。但是,是的,它可以很好地作为函数延迟器。
scope.setTimeout
函数,如果省略 delay
参数,则默认使用 0
的值。
在这里你有简单的解决方案(其他写一下)http://www.benlesh.com/2012/05/calling-javascript-function.html
在这里,您有上述现成的解决方案:
function async(your_function, callback) {
setTimeout(function() {
your_function();
if (callback) {callback();}
}, 0);
}
测试 1(可能输出“1 x 2 3”或“1 2 x 3”或“1 2 3 x”):
console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);
测试 2(将始终输出“x 1”):
async(function() {console.log('x');}, function() {console.log(1);});
此函数以超时 0 执行 - 它将模拟异步任务
console.log(1)
并且输出 (undefined
) 作为第二个参数传递给 async()
。在 TEST 1 的情况下,我认为您并不完全了解 JavaScript 的执行队列。因为对 console.log()
的每个调用都发生在同一个堆栈中,所以 x
保证最后被记录。我会否决这个错误信息的答案,但没有足够的代表。
async(function() {console.log('x')}, function(){console.log(1)});
。
TEST 2 as: async(function() {console.log('x')}, function(){console.log(1)});
- 我已经更正了
这是一个函数,它接收另一个函数并输出一个异步运行的版本。
var async = function (func) {
return function () {
var args = arguments;
setTimeout(function () {
func.apply(this, args);
}, 0);
};
};
它被用作制作异步函数的简单方法:
var anyncFunction = async(function (callback) {
doSomething();
callback();
});
这与@fider 的答案不同,因为函数本身有自己的结构(没有添加回调,它已经在函数中),还因为它创建了一个可以使用的新函数。
(function(a){ asyncFunction(a); })(a)
setTimeout(asyncFunction, 0, a);
迟到了,但是为了在 ES6 中介绍 promises
后展示一个简单的解决方案,它处理异步调用要容易得多:
你在一个新的 Promise 中设置了异步代码:
var asyncFunct = new Promise(function(resolve, reject) {
$('#link').animate({ width: 200 }, 2000, function() {
console.log("finished");
resolve();
});
});
请注意在异步调用完成时设置 resolve()
。
然后在 Promise 的 .then()
内添加要在异步调用完成后运行的代码:
asyncFunct.then((result) => {
console.log("Exit");
});
这是它的一个片段:
$('#link').click(function () { console.log("Enter"); var asyncFunct = new Promise(function(resolve, reject) { $('#link').animate({ width: 200 }, 2000, function() { console.log("finished"); resolve(); }); }); asyncFunct.then((result) => { console.log("Exit"); }); } ); 链接 移动
new Promise)
的参数立即运行,而不是在下一个滴答声中。所以我不确定这个答案是否正确。但是,看起来 then 处理程序总是在运行稍后打勾。
如果您想使用参数并调节异步函数的最大数量,您可以使用我构建的简单异步工作者:
var BackgroundWorker = function(maxTasks) {
this.maxTasks = maxTasks || 100;
this.runningTasks = 0;
this.taskQueue = [];
};
/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
var self = this;
if(self.runningTasks >= self.maxTasks) {
self.taskQueue.push({ task: task, delay: delay, params: params});
} else {
self.runningTasks += 1;
var runnable = function(params) {
try {
task(params);
} catch(err) {
console.log(err);
}
self.taskCompleted();
}
// this approach uses current standards:
setTimeout(runnable, delay, params);
}
}
BackgroundWorker.prototype.taskCompleted = function() {
this.runningTasks -= 1;
// are any tasks waiting in queue?
if(this.taskQueue.length > 0) {
// it seems so! let's run it x)
var taskInfo = this.taskQueue.splice(0, 1)[0];
this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
}
}
你可以像这样使用它:
var myFunction = function() {
...
}
var myFunctionB = function() {
...
}
var myParams = { name: "John" };
var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);
Function.prototype.applyAsync = function(params, cb){
var function_context = this;
setTimeout(function(){
var val = function_context.apply(undefined, params);
if(cb) cb(val);
}, 0);
}
// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);
虽然与其他解决方案没有根本不同,但我认为我的解决方案还有一些额外的好处:
它允许函数的参数
它将函数的输出传递给回调
它被添加到 Function.prototype 允许更好的方式来调用它
此外,与内置函数 Function.prototype.apply
的相似性对我来说似乎很合适。
除了@pimvdb 的出色回答,如果您想知道,async.js 也不提供真正的异步函数。这是库主要方法的(非常)精简版本:
function asyncify(func) { // signature: func(array)
return function (array, callback) {
var result;
try {
result = func.apply(this, array);
} catch (e) {
return callback(e);
}
/* code ommited in case func returns a promise */
callback(null, result);
};
}
因此该函数可以防止错误并优雅地将其交给回调处理,但代码与任何其他 JS 函数一样是同步的。
不幸的是,JavaScript 不提供异步功能。它仅适用于单个线程。但大多数现代浏览器都提供 Worker
s,这是在后台执行并可以返回结果的第二个脚本。所以,我找到了一个解决方案,我认为异步运行一个函数很有用,它为每个异步调用创建一个工作线程。
下面的代码包含函数 async
在后台调用。
Function.prototype.async = function(callback) {
let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
let worker = new Worker(window.URL.createObjectURL(blob));
worker.addEventListener("message", function(e) {
this(e.data.result);
}.bind(callback), false);
return function() {
this.postMessage(Array.from(arguments));
}.bind(worker);
};
这是一个使用示例:
(function(x) {
for (let i = 0; i < 999999999; i++) {}
return x * 2;
}).async(function(result) {
alert(result);
})(10);
这将执行一个函数,该函数迭代一个具有巨大数字的 for
以花费时间作为异步性的演示,然后获取传递数字的双倍。 async
方法提供了一个 function
,它在后台调用想要的函数,并且在作为 async
的参数提供的函数中回调其唯一参数中的 return
。所以在回调函数中我alert
得到了结果。
MDN 有一个关于使用 setTimeout 保留“this”的good example。
如下所示:
function doSomething() {
// use 'this' to handle the selected element here
}
$(".someSelector").each(function() {
setTimeout(doSomething.bind(this), 0);
});
setInterval
),但我们甚至无法打开它来查看它是如何完成的。你碰巧有更多关于这个主题的信息吗?setImmediate
promises
呢?它给出awaitable
吗?