【问题标题】:Proper way to wait for one function to finish before continuing?在继续之前等待一个功能完成的正确方法?
【发布时间】:2014-02-26 09:49:11
【问题描述】:

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

【问题讨论】:

  • firstFunction 究竟做了什么使它异步?无论哪种方式 - 检查承诺
  • 第一个函数是每 10 秒快速更新一个分数时钟。哪个...想一想,我想我们可以预先计算,然后通过 setTimeout 手动暂停第二个函数调用。也就是说,我仍然可以看到在其他地方拥有暂停能力的愿望。
  • 你不需要暂停 - google for promises in jquery
  • @zerkms 承诺看起来很有趣!仍在调查浏览器支持...
  • 我也有同样的问题。

标签: javascript jquery delay pausing-execution


【解决方案1】:

试试这个

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, ...
}

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

【讨论】:

  • 这比OP的代码好多少?
【解决方案2】:

等待一个函数首先完成的一种优雅方法是将Promisesasync/await 函数一起使用。


  1. 首先,创建一个Promise。 我创建的功能将在 2s 后完成。我用了 setTimeout为了演示的情况 执行指令需要一些时间。
  2. 对于第二个功能,您可以使用 async/await 一个函数,您将在其中 await 以完成第一个函数 在继续执行说明之前。

示例:

    //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 Promise 的值为 y,然后我可以在第二个函数中使用它。

【讨论】:

  • 是的,这将始终有效。如果有人涉及这种场景,那么 JavaScript Promise 将为他们解决问题。
  • 很好的解决方案。
  • IMO 这个问题的最佳答案 - 谢谢。
【解决方案3】:

可以使用承诺

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

承诺可以有

已完成 - 操作已成功完成

拒绝 - 操作失败

待定 - 两个操作均未完成

已解决 - 已完成或被拒绝

【讨论】:

  • 这对 Jakub 的回答几乎没有什么帮助,也没有多大帮助。
【解决方案4】:

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

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

【讨论】:

  • 这对我有用,是的。好点,我想知道为什么没有人提到这一点。并非所有时候都想使用同步/异步
  • 谢谢!!工作正常!
  • 我不熟悉 JQuery(我认为这是使用的,基于 jsfiddle 演示中的设置),并且对它的工作原理感到非常困惑。有人可以解释为什么这些函数没有名称,以及为什么整个代码 sn-p 都在括号中吗? (function(next)...) 另外,next 在从未声明的情况下如何工作?是原生的 JQuery 函数吗?
  • @velkoon,它与 jQuery 没有任何关系,但它是纯 javascript。结构或模式称为self-invoking functionnext 参数实际上是声明的,如果你仔细看,第二个function() 是函数本身注入的next。任何需要在其他内容之后执行的地方,您可以复制粘贴前 4 行,然后将要执行的代码放在两个 {...} 文字之间。
  • 这是最好的答案。你是最好的
【解决方案5】:

您的主要乐趣将调用 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');
            });
        }

【讨论】:

    【解决方案6】:

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

    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(() =&gt; console.log('huzzah, I\'m done!'))


    更新:我很久以前就回答了这个问题,真的很想更新它。虽然回调绝对没问题,但根据我的经验,它们往往会导致代码更难以阅读和维护。在某些情况下,我仍然使用它们,例如将进度事件等作为参数传递。此更新只是为了强调替代方案。

    原来的问题也没有特别提到异步,所以如果有人感到困惑,如果你的函数是同步的,它在调用时阻塞。例如:

    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
    }
    

    IMO,async/await 使您的代码比直接使用 Promise 更具可读性(大多数情况下)。如果您需要处理捕获错误,请将其与 try/catch 一起使用。在这里阅读更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

    【讨论】:

    • 回调不方便处理异步,promise 更简单灵活
    • 虽然您说得对,我不应该使用最好(更新)这个词,但回调与承诺的便利性取决于问题的复杂性。
    • 它变得离题了,但我个人认为这些天没有理由更喜欢回调 :-) 不记得我在过去 2 年编写的任何代码都提供了回调而不是承诺。
    • 如果需要,可以在 es6 中使用箭头函数 secondFunction(){ firstFunction((response) => { console.log(response); }); }
    • @JanakaPushpakumara 更新了我的旧答案。
    【解决方案7】:

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

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

    【讨论】:

    • 请至少添加一个高级 cmets 来描述给定的 sn-p。这是为了解释您的代码如何解决问题。
    • 问题是在第一个函数之后运行第二个函数。我展示了如何在第一个函数之后运行第二个函数,以及如何在第二个函数之后运行第三个函数。我做了三个函数,方便理解需要什么代码。
    【解决方案8】:

    使用异步/等待:

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

    【讨论】:

    • 对于那些坚持支持旧版浏览器的人来说,IE 不支持 async/await
    • 这可以在两个函数之外使用吗,如$(function() { await firstFunction(); secondFunction(); });
    • @Lewistrick 请记住 await 只能在 async 方法中使用。因此,如果您将父函数设为async,您可以在内部调用任意数量的async methods,无论是否使用await
    • 可以awaitsetTimeout吗?
    • @Shayan 是的,将 setTimeout 包装在另一个函数中,该函数在超时后解决一个承诺。
    【解决方案9】:

    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。加载 div 闪烁并且它消失了,而 ajax 调用尚未完成,即使 makeAjaxCall() 已返回。 复杂性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?

    【讨论】:

    • 对于反对者,我很想知道这个答案有什么问题。
    • 这是一个旧的回复,我还没有投票,但如果您提供对函数 1、2、3 之前/之后的洞察力,而不是仅仅描述它用词。此外,使用回调可能是有意义的——这样,您可以将 function1 和 function3 传递给 function2,这样一个在开始时执行,另一个在异步操作返回时执行。
    【解决方案10】:

    看来您在这里遗漏了重要的一点: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 已经是 falsefirstFunction 将返回。那是因为你不能从for 循环(// do something)内部“屈服”,循环可能不会被中断,必须先完全完成(更多细节:Javascript thread-handling and race-conditions)。

    也就是说,您仍然可以使 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 关键字作为 generators 的一部分。这将允许在其他同步的 JavaScript 代码流 (more details and an example) 中“打孔”异步漏洞。但是,浏览器对生成器的支持目前仅限于 Firefox 和 Chrome、AFAIK。

    【讨论】:

    • 你救了我。 $.Deferred() 是我一直在追求的。谢谢
    • @noseratio 我试过这个。但是 waitForIt 方法根本不会被调用。我做错了什么?
    • @vigamage,你能提供一个链接到你尝试过的 jsfiddle 或 codepen 吗?
    • 您好,感谢您的关注。我让它工作了。谢谢你..!
    • @LodeAlaert 这个答案按照今天的标准来说有点过时了。你应该使用原生 JavaScript Promises 和 async/await 语法。网络上有很多好的教程,例如,试试 www.freecodecamp.org。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多