如何在Javascript中创建异步函数?

时间:2012-03-01 13:16:47

标签: javascript jquery function asynchronous

查看此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");    
});

正如您在控制台中看到的,“animate”函数是异步的,它“分叉”事件处理程序块代码的流程。事实上:

$('#link').click(function () {
    console.log("Enter");
    asyncFunct();
    console.log("Exit");    
});

function asyncFunct() {
    console.log("finished");
}

按照块代码的流程进行操作!

如果我希望使用此行为创建function asyncFunct() { },我该如何使用javascript / jquery?我认为没有使用setTimeout()的策略     

12 个答案:

答案 0 :(得分:173)

您无法创建真正的自定义异步功能。您最终必须利用本机提供的技术,例如:

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • 一些HTML5 API,例如File API,Web Database API
  • 支持onload
  • 的技术
  • ......很多其他人

事实上,对于动画jQuery uses setInterval

答案 1 :(得分:64)

您可以使用计时器:

setTimeout( yourFn, 0 );

(其中yourFn是对您的函数的引用)

或,Lodash

_.defer( yourFn );
  

延迟调用func,直到当前调用堆栈清除为止。 <{1}}在调用时会提供任何其他参数。

答案 2 :(得分:28)

这里你有简单的解决方案(其他写的) 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);

TEST 2(将始终输出'x 1'):

async(function() {console.log('x');}, function() {console.log(1);});

此函数在超时0时执行 - 它将模拟异步任务

答案 3 :(得分:9)

这是一个函数,它接受另一个函数并输出一个运行异步的版本。

var async = function (func) {
  return function () {
    var args = arguments;
    setTimeout(function () {
      func.apply(this, args);
    }, 0);
  };
};

它被用作制作异步函数的简单方法:

var anyncFunction = async(function (callback) {
    doSomething();
    callback();
});

这与@ fider的答案不同,因为函数本身有自己的结构(没有添加回调,它已经在函数中),也因为它创建了一个可以使用的新函数。

答案 4 :(得分:5)

This page引导您完成创建异步javascript函数的基础知识。

从ES2017开始,异步javacript函数更容易编写。您还应该在Promises上阅读更多内容。

答案 5 :(得分:3)

编辑:我完全误解了这个问题。在浏览器中,我会使用setTimeout。如果它在另一个线程中运行很重要,我会使用Web Workers

答案 6 :(得分:3)

如果你想使用参数并规范异步函数的最大数量,你可以使用一个简单的异步工作者:

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);

答案 7 :(得分:3)

最近,但是为了在ES6中引入<div id="parent"> <div id="child1">Here goes the correct answer</div>(button element will also work fine) </div> 后使用promises显示一个简单的解决方案,它可以更轻松地处理异步调用:

您可以在新承诺中设置异步代码:

var asyncFunct = new Promise(function(resolve, reject) {
    $('#link').animate({ width: 200 }, 2000, function() {
        console.log("finished");                
        resolve();
    });             
});

注意在异步调用完成时设置resolve() 然后,在承诺的.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");    
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" id="link">Link</a>
<span>Moving</span>

JSFiddle

答案 8 :(得分:2)

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的相似性似乎对我来说也是合适的。

答案 9 :(得分:1)

在@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函数一样同步。

答案 10 :(得分:1)

不幸的是,JavaScript不提供异步功能。它仅适用于单个线程。但是大多数现代浏览器都提供Worker s ,这是第二个在后台执行并可以返回结果的脚本。 所以,我找到了一个解决方案,我认为异步运行一个函数很有用,它为每个异步调用创建一个worker。

以下代码包含在后台调用的功能 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得到了结果。

答案 11 :(得分:0)

MDN对使用setTimeout保留“this”有一个good example

如下所示:

function doSomething() {
    // use 'this' to handle the selected element here
}

$(".someSelector").each(function() {
    setTimeout(doSomething.bind(this), 0);
});