在继续之前等待一个功能完成的正确方法?

我有两个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?

1008201 次浏览

处理异步工作的一种方法是使用回调函数,例如:

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

这里您似乎忽略了重要的一点: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循环(// do something)内部“屈服”,循环可能不会被中断,必须先完全完成(详细信息: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。

承诺的唯一问题是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

这不是你想要的。它会立即执行函数1、函数2和函数3。加载div会闪烁,并且它已经消失,而ajax调用还没有完成,即使makeAjaxCall()已经返回。< em >并发症makeAjaxCall()已经把它的工作分解成块,这些块在主JavaScript线程的每次旋转中一点点地推进——它的行为是异步的。但是,同一主线程在一次旋转/运行期间,可以快速且可预测地执行同步部分。

我处理这件事的方式:就像我说的,函数是工作的原子单位。我把函数1和函数2的代码结合在一起——我把函数1的代码放在函数2中,在asynch调用之前。我消去了函数1。包括异步调用在内的所有内容都按顺序可预测地执行。

然后,当异步调用完成时,在主JavaScript线程多次旋转之后,让它调用函数3。这保证了秩序。例如,在ajax中,onreadystatechange事件处理程序被多次调用。当它报告它已经完成时,然后调用你想要的最后一个函数。

我同意这比较乱。我喜欢让代码是对称的,我喜欢让函数做一件事(或接近它),我不喜欢让ajax调用以任何方式负责显示(创建对调用者的依赖)。但是,在同步函数中嵌入异步调用时,为了保证执行顺序,必须做出一些妥协。我必须为ie10编写代码,所以不能保证。

< em >总结:对于同步调用,保证顺序是微不足道的。每个函数都按照被调用的顺序完全执行。对于具有异步调用的函数,保证顺序的唯一方法是监视异步调用何时完成,并在检测到该状态时调用第三个函数

有关JavaScript线程的讨论,请参见:https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

另外,关于这个主题,还有一个类似的、评分很高的问题:我应该如何调用3个函数,以便执行他们一个接一个?

使用async/await:

async function firstFunction(){
for(i=0;i<x;i++){
// do something
}
return;
};

然后在你的其他函数中使用await来等待它返回:

async function secondFunction(){
await firstFunction();
// now wait for firstFunction to finish...
// do something else
};

等待一个函数先完成的一种优雅方法是使用承诺异步/等待函数。


    首先,创建一个承诺。 我创建的函数将在2s后完成。我使用 setTimeout,以演示的情况 . .
  1. 对于第二个函数,可以使用 async/await 一个你将await作为第一个函数完成的函数
  2. . .

例子:

    //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,如resolve()。在我的例子中,我resolved值为yPromise,然后我可以在第二个函数中使用它。

这就是我想到的,因为我需要在一个链中运行几个操作。

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

你的主函数将调用firstFunction,然后完成它,你的下一个函数将执行。

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

我想知道为什么没有人提到这个简单的模式?:

(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/

可以使用承诺

承诺是表示未来未知值的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;
})

一个承诺可以有

完成——动作成功完成

拒绝——操作失败

未决-两个操作都未完成

已解决——已经完成或被拒绝

试试这个

function firstFunction(){
    

// do something
X=true;
return X;
}


function secondFunction(){
X=false;
X=firstFunction();
setTimeout( function() {
if(X==true){
// do something else
}else{
// do something else
}
alert(X); // To check X
       

}, 100); // Can increase time delay 200,300, ...
}


增加时间 from One hundred. 200,300, 基于firstFunction完成所需的时间

我也有同样的问题。
我的解决方案是在异步函数的末尾调用我的第二个函数,
因为你已经在async函数中有一个For循环,
一旦循环条件的最大值达到
,就可以调用第二个函数 在你的例子中哪一个是
I = x-1 所以:< / p >
function firstFunction(){
for(i=0;i<x;i++){
// do something
if(i === x - 1){
// do something else or call secondFunction();
}
}
}
经过两天的努力,我终于写下了这篇文章。这是最好的办法 试着这样做:

var recv = -1;


async function First_fn() {


var answ = await Second_fn();
console.log(answ);
recv = -1;


}


async function Second_fn() {
var parm = 1;
var adrss = 2;


await Third_fn(adrss, parm);
// Using the following loop will minimize the waiting time
for (let i = 0; i < 180; i++) {
if (recv !== -1) {
console.log(recv);
break;
}
else {
await new Promise(resolve => setTimeout(resolve, 500));
}
}
return recv;
}


async function Third_fn(adrss, parm) {
//someting to do => result
//for example:
let result = adrss + parm;
recv = result; // recv is global varable
}
 

First_fn();