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


当前回答

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

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。

其他回答

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

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

使用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
};

我也有同样的问题。 我的解决方案是在async函数的末尾调用第二个函数, 因为你已经在async函数中有了一个For循环, 一旦达到循环条件的最高值,就可以调用第二个函数 你的情况是什么 I = x-1 所以:

function firstFunction(){
  for(i=0;i<x;i++){
     // do something
     if(i === x - 1){
       // do something else or call secondFunction();
     }
  }
}

等待一个函数先完成的一种优雅方法是使用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个函数,以便一个接一个地执行它们?