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

时间:2014-02-03 01:11:29

标签: javascript jquery delay pausing-execution

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

10 个答案:

答案 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主线程将按执行顺序在代码中出现的顺序完全执行一个功能。保证同步功能的顺序很简单-每个功能将完全按照其调用的顺序执行。

将同步功能视为工作的基本单位。 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-43c85fce7e23https://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)

等待一个功能首先完成的一种优雅方法是将Promisesasync/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>