我有两个JS函数。一个打电话给另一个。在调用函数中,我想调用另一个函数,等待该函数完成,然后继续。因此,例如/伪代码:
function firstFunction(){
for(i=0;i<x;i++){
// do something
}
};
function secondFunction(){
firstFunction()
// now wait for firstFunction to finish...
// do something else
};
我想出了这个解决方案,但不知道这是否是一个聪明的方法。
var isPaused = false;
function firstFunction(){
isPaused = true;
for(i=0;i<x;i++){
// do something
}
isPaused = false;
};
function secondFunction(){
firstFunction()
function waitForIt(){
if (isPaused) {
setTimeout(function(){waitForIt()},100);
} else {
// go do that thing
};
}
};
这合法吗?有没有更优雅的处理方式?也许是用jQuery?
这里您似乎忽略了重要的一点:JavaScript是一个单线程执行环境。让我们再看一遍你的代码,注意我添加了alert(“Here”):
var isPaused = false;
function firstFunction(){
isPaused = true;
for(i=0;i<x;i++){
// do something
}
isPaused = false;
};
function secondFunction(){
firstFunction()
alert("Here");
function waitForIt(){
if (isPaused) {
setTimeout(function(){waitForIt()},100);
} else {
// go do that thing
};
}
};
你不需要等待isPaused。当你看到“Here”警告时,isPaused将为false, firstFunction将返回。这是因为你不能从for循环内部“屈服”(//做某事),循环可能不会被中断,必须先完全完成(更多细节:Javascript线程处理和竞争条件)。
也就是说,您仍然可以使firstFunction内部的代码流是异步的,并使用回调或承诺通知调用方。你必须放弃for循环,用if (JSFiddle)来模拟它:
function firstFunction()
{
var deferred = $.Deferred();
var i = 0;
var nextStep = function() {
if (i<10) {
// Do something
printOutput("Step: " + i);
i++;
setTimeout(nextStep, 500);
}
else {
deferred.resolve(i);
}
}
nextStep();
return deferred.promise();
}
function secondFunction()
{
var promise = firstFunction();
promise.then(function(result) {
printOutput("Result: " + result);
});
}
另一方面,JavaScript 1.7引入了yield关键字作为生成器的一部分。这将允许在同步JavaScript代码流中“打孔”异步(更多细节和示例)。然而,浏览器对生成器的支持目前仅限于Firefox和Chrome, AFAIK。
处理异步工作的一种方法是使用回调函数,例如:
function firstFunction(_callback){
// do some asynchronous work
// and when the asynchronous stuff is complete
_callback();
}
function secondFunction(){
// call first function and pass in a callback function which
// first function runs when it has completed
firstFunction(function() {
console.log('huzzah, I\'m done!');
});
}
根据@Janaka Pushpakumara的建议,你现在可以使用箭头函数来实现同样的事情。例如:
firstFunction(() => console.log('huzzah, I\'m done!'))
更新:我很早就回答了这个问题,真的很想更新它。虽然回调是绝对好的,但根据我的经验,它们往往会导致代码更难阅读和维护。但在某些情况下,我仍然使用它们,例如将进度事件作为参数传递。这次更新只是强调替代方案。
另外,最初的问题并没有特别提到async,所以如果有人感到困惑,如果你的函数是同步的,它将在调用时阻塞。例如:
doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()
如果正如暗示的那样,函数是异步的,那么我现在处理所有异步工作的方式是async/await。例如:
const secondFunction = async () => {
const result = await firstFunction()
// do something else here after firstFunction completes
}
在我看来,async/await使你的代码比直接使用promises(大多数时候)更具可读性。如果你需要处理捕获错误,那么使用try/catch。更多信息请点击:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function。
这就是我想到的,因为我需要在一个链中运行几个操作。
<button onclick="tprom('Hello Niclas')">test promise</button>
<script>
function tprom(mess) {
console.clear();
var promise = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(mess);
}, 2000);
});
var promise2 = new Promise(async function (resolve, reject) {
await promise;
setTimeout(function () {
resolve(mess + ' ' + mess);
}, 2000);
});
var promise3 = new Promise(async function (resolve, reject) {
await promise2;
setTimeout(function () {
resolve(mess + ' ' + mess+ ' ' + mess);
}, 2000);
});
promise.then(function (data) {
console.log(data);
});
promise2.then(function (data) {
console.log(data);
});
promise3.then(function (data) {
console.log(data);
});
}
</script>
等待一个函数先完成的一种优雅方法是使用Promises和async/await函数。
首先,创建一个承诺。
我创建的函数将在2s后完成。我使用
setTimeout来演示
指令需要一些时间来执行。
对于第二个函数,您可以使用
异步/等待
等待第一个函数完成的函数
在继续执行指令之前。
例子:
//1. Create a new function that returns a promise
function firstFunction() {
return new Promise((resolve, reject) => {
let y = 0
setTimeout(() => {
for (i=0; i<10; i++) {
y++
}
console.log('Loop completed.')
resolve(y)
}, 2000)
})
}
//2. Create an async function
async function secondFunction() {
console.log('Before promise call.')
//3. Await for the first function to complete
const result = await firstFunction()
console.log('Promise resolved: ' + result)
console.log('Next step.')
};
secondFunction()
注意:
您可以像resolve()那样简单地解析Promise而不包含任何值。在我的例子中,我用y值解析了Promise,然后可以在第二个函数中使用y值。
承诺的唯一问题是IE不支持它们。Edge可以,但IE 10和11还有很多:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise(兼容性在底部)
JavaScript是单线程的。如果您没有进行异步调用,那么它的行为将是可预测的。在执行下一个函数之前,主JavaScript线程将按照它们在代码中出现的顺序完全执行一个函数。保证同步函数的顺序是微不足道的——每个函数将完全按照它被调用的顺序执行。
将同步函数视为工作的原子单元。主JavaScript线程将按照语句在代码中出现的顺序完全执行它。
但是,加入异步调用,如下所示:
showLoadingDiv(); // function 1
makeAjaxCall(); // function 2 - contains async ajax call
hideLoadingDiv(); // function 3
This doesn't do what you want. It instantaneously executes function 1, function 2, and function 3. Loading div flashes and it's gone, while the ajax call is not nearly complete, even though makeAjaxCall() has returned. THE COMPLICATION is that makeAjaxCall() has broken its work up into chunks which are advanced little by little by each spin of the main JavaScript thread - it's behaving asychronously. But that same main thread, during one spin/run, executed the synchronous portions quickly and predictably.
我处理它的方法是,就像我说的,函数是功的原子单位。我把函数1和函数2的代码结合在一起——我把函数1的代码放在函数2中,在asynch调用之前。我消去了函数1。包括异步调用在内的所有内容都按顺序可预测地执行。
然后,当异步调用完成时,在主JavaScript线程多次旋转之后,让它调用函数3。这保证了秩序。例如,在ajax中,onreadystatechange事件处理程序被多次调用。当它报告它已经完成时,然后调用你想要的最后一个函数。
我同意这比较乱。我喜欢让代码是对称的,我喜欢让函数做一件事(或接近它),我不喜欢让ajax调用以任何方式负责显示(创建对调用者的依赖)。但是,在同步函数中嵌入异步调用时,为了保证执行顺序,必须做出一些妥协。我必须为ie10编写代码,所以不能保证。
总结:对于同步调用,保证顺序是微不足道的。每个函数都按照被调用的顺序完全执行。对于带有异步调用的函数,保证顺序的唯一方法是监视异步调用何时完成,并在检测到该状态时调用第三个函数。
关于JavaScript线程的讨论,请参见:https://medium.com/@francesco_rizzi/ JavaScript -main-thread- -43c85fce7e23和https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
另外,关于这个主题还有一个类似的、评分很高的问题:我应该如何调用3个函数,以便一个接一个地执行它们?