我有两个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?
答案 0 :(得分:105)
处理这样的异步工作的一种方法是使用回调函数,例如:
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!'))
答案 1 :(得分:45)
看来你在这里忽略了一个重点: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
。当您看到“此处”提醒时,isPaused
已经false
,并且firstFunction
将返回。那是因为你无法从for
循环(// do something
)中“屈服”,循环可能不会被中断并且必须首先完全完成(更多细节:Javascript thread-handling and race-conditions)。
也就是说,您仍然可以使firstFunction
内的代码流异步,并使用callback或promise来通知调用者。您必须放弃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
关键字作为generators的一部分。这将允许在其他同步JavaScript代码流(more details and an example)中“打击”异步漏洞。但是,对生成器的浏览器支持目前仅限于Firefox和Chrome,AFAIK。
答案 2 :(得分:28)
使用异步/等待:
async function firstFunction(){
for(i=0;i<x;i++){
// do something
}
return;
};
然后在其他函数中使用wait等待它返回:
async function secondFunction(){
await firstFunction();
// now wait for firstFunction to finish...
// do something else
};
答案 3 :(得分:3)
promise的唯一问题是IE不支持它们。 Edge确实可以,但是有很多IE 10和11:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise(底部兼容)
因此,JavaScript是单线程的。如果您不进行异步调用,它将可以预期地运行。 JavaScript主线程将按执行顺序在代码中出现的顺序完全执行一个功能。保证同步功能的顺序很简单-每个功能将完全按照其调用的顺序执行。
将同步功能视为工作的基本单位 strong>。 JavaScript主线程将按照语句在代码中出现的顺序完全执行它。
但是,引发异步调用,如下所示:
showLoadingDiv(); // function 1
makeAjaxCall(); // function 2 - contains async ajax call
hideLoadingDiv(); // function 3
这不能满足您的要求。它立即执行功能1,功能2和功能3。即使ajax调用还没有完成,即使返回了makeAjaxCall()
,加载div闪烁并消失了。 复杂 是,makeAjaxCall()
已将其工作分解为多个块,每个主JavaScript线程的每次旋转都会逐步推进这些块-行为异常。但是,同一主线程在一次旋转/运行期间可以快速且可预测地执行同步部分。
所以,我的处理方式:就像我说的那样,函数是工作的原子单元。我将函数1和2的代码合并在一起-在异步调用之前,将函数1的代码放入函数2中。我摆脱了功能1。直到异步调用为止的所有工作都可以按顺序执行。
然后,当异步调用完成时,在主JavaScript线程旋转几次之后,让其调用函数3。这样可以保证顺序。例如,对于ajax,onreadystatechange事件处理程序被多次调用。报告完成后,请调用所需的最终函数。
我同意这比较麻烦。我喜欢让代码对称,我喜欢让函数做一件事情(或接近它),而且我不喜欢让ajax调用以任何方式负责显示(对调用者产生依赖性)。但是,将异步调用嵌入到同步函数中,因此必须做出折衷以保证执行顺序。而且我必须为IE 10编写代码,所以没有任何希望。
摘要 :对于同步呼叫,保证顺序很简单。每个函数均按其调用顺序完全执行。对于具有异步调用的函数,保证顺序的唯一方法是监视异步调用何时完成,并在检测到该状态时调用第三个函数。
有关JavaScript线程的讨论,请参见:https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23和https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
关于此主题,另一个类似的,评分很高的问题:How should I call 3 functions in order to execute them one after the other?
答案 4 :(得分:2)
等待一个功能首先完成的一种优雅方法是将Promises与async/await函数一起使用。
简单示例:
let firstFunction = new Promise(function(resolve, reject) {
let x = 10
let y = 0
setTimeout(function(){
for(i=0;i<x;i++){
y++
}
console.log('loop completed')
resolve(y)
}, 2000)
})
首先,创建一个Promise。上面的功能将在2秒钟后完成。我使用setTimeout
来说明这种情况下指令将需要一些时间来执行(根据您的示例)。
async function secondFunction(){
let result = await firstFunction
console.log(result)
console.log('next step')
};
secondFunction()
对于第二个功能,可以使用async/await功能,在继续操作说明之前,您将等待完成第一个功能。
注意:您可以简单地解决Promise,而无需像resolve()
这样的任何值。在我的示例中,我使用y
的值解析了Promise,然后可以在第二个函数中使用它。
希望它对仍然访问此线程的人有所帮助。
答案 5 :(得分:2)
可以使用Promise
promise 是一种 JavaScript 构造,表示未来的未知值。它可能是 API 调用的结果,也可能是网络请求失败的错误对象。你一定会有所收获。
const promise = new Promise((resolve, reject) => {
// Make a network request
if (yourcondition=="value") {
} else {
reject(error);
}
})
promise.then(res => {
//if not rejected, code
}).catch(err => {
//return false;
})
承诺可以
已完成 - 操作成功完成
被拒绝 - 操作失败
pending - 两个操作都没有完成
已解决 - 已完成或被拒绝
答案 6 :(得分:1)
您的主要乐趣将呼叫firstFun,然后在完成后您的下一个乐趣将呼叫。
async firstFunction() {
const promise = new Promise((resolve, reject) => {
for (let i = 0; i < 5; i++) {
// do something
console.log(i);
if (i == 4) {
resolve(i);
}
}
});
const result = await promise;
}
second() {
this.firstFunction().then( res => {
// third function call do something
console.log('Gajender here');
});
}
答案 7 :(得分:1)
我想知道为什么没有人提到这种简单的模式? :
(function(next) {
//do something
next()
}(function() {
//do some more
}))
仅将超时用于盲目的等待是不明智的做法;涉及承诺只会增加代码的复杂性。在OP的情况下:
(function(next) {
for(i=0;i<x;i++){
// do something
if (i==x-1) next()
}
}(function() {
// now wait for firstFunction to finish...
// do something else
}))
一个小演示-> http://jsfiddle.net/5jdeb93r/
答案 8 :(得分:0)
我对过程真的很困惑...考虑到我有一个内部函数,如何将结果(真/假)返回给外部函数!?!
function confirmar2(texto)
{
var modalConfirm = function(callback){
$("#modalConfirmacaoTexto").html(texto);
$("#modalConfirmacaoBotaoSim").unbind("click");
$("#modalConfirmacaoBotaoSim").on("click", function(){callback(true);$("#modalConfirmacao").modal('hide');return;});
$("#modalConfirmacaoBotaoNao").unbind("click");
$("#modalConfirmacaoBotaoNao").on("click", function(){callback(false);$("#modalConfirmacao").modal('hide');return;});
$('#modalConfirmacao').modal('show');
};
modalConfirm(function(confirm){
if(confirm)
{
return true;
}
else
{
return false;
}
});
}
答案 9 :(得分:-1)
这是我想出的,因为我需要在链中运行多个操作。
<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>