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

IT技术 javascript jquery delay pausing-execution
2021-01-15 07:10:03

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

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(() => 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

它变得离题了,但我个人认为这些天没有理由更喜欢回调 :-) 不记得我在过去 2 年中编写的任何代码提供的回调超过Promise。
2021-03-19 07:10:03
虽然我不应该使用最佳(更新)这个词是对的,但回调与Promise的便利性取决于问题的复杂性。
2021-03-22 07:10:03
如果你愿意,你可以在 es6 secondFunction(){ firstFunction((response) => { console.log(response); }); 中使用箭头函数。}
2021-03-25 07:10:03
@JanakaPushpakumara 更新了我的旧答案。
2021-03-27 07:10:03
回调不方便处理异步,promise 更容易和灵活
2021-03-28 07:10:03

使用异步/等待:

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
};
这可以在两个函数之外使用,例如,$(function() { await firstFunction(); secondFunction(); });
2021-03-22 07:10:03
@Shayan 是的,将 setTimeout 包装在另一个函数中,该函数在超时后解决Promise。
2021-03-23 07:10:03
是否有可能await一个setTimeout
2021-03-24 07:10:03
@Lewistrick 请记住await只能在async方法中使用。因此,如果您创建父函数async,则可以async methods在内部调用任意数量的函数await,无论是否需要。
2021-03-30 07:10:03
对于我们这些坚持支持旧浏览器的人,IE 不支持 async/await
2021-04-08 07:10:03

看来您在这里错过了一个重要的点: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当您看到“此处”警报时,isPausedfalse已经firstFunction返回,并且将返回。那是因为你不能从for循环内部“屈服” // do something),循环可能不会被中断,并且必须首先完全完成(更多细节:Javascript 线程处理和竞争条件)。

也就是说,您仍然可以使内部的代码流firstFunction异步并使用回调或Promise通知调用者。您必须放弃for循环并使用ifJSFiddle代替模拟它

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 代码流中“打孔”异步漏洞(更多细节和示例)。但是,浏览器对生成器的支持目前仅限于 Firefox 和 Chrome,AFAIK。

你救了我。$.Deferred() 是我一直在追求的。谢谢
2021-03-18 07:10:03
@noseratio 我试过这个。但是 waitForIt 方法根本没有被调用。我究竟做错了什么?
2021-03-21 07:10:03
@LodeAlaert 这个答案以今天的标准来说有点过时了。您应该使用原生 JavaScript Promises 和 async/await 语法。网上有很多很好的教程,例如,试试 www.freecodecamp.org。
2021-03-22 07:10:03
@vigamage,您能否提供一个指向 jsfiddle 或 codepen 的链接,说明您尝试过的内容?
2021-03-24 07:10:03
您好,感谢您的关心。我让它工作了。谢谢..!
2021-03-26 07:10:03

等待一个函数先完成的一种优雅方式是使用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()


笔记:

你可以简单地resolvePromise没有像这样的任何值resolve()在我的例子,我resolvedPromise用的valuey,我可以然后在第二个函数中使用。

很好的解决方案。
2021-03-29 07:10:03
是的,这将始终有效。如果有人参与了这种场景,那么 JavaScript Promise 将为他们解决问题。
2021-03-30 07:10:03
这个问题的最佳答案 IMO - 谢谢。
2021-04-06 07:10:03

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

(function(next) {
  //do something
  next()
}(function() {
  //do some more
}))

仅仅为了盲目等待而使用超时是不好的做法;并且涉及Promise只会增加代码的复杂性。在 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/

谢谢!!工作正常!!
2021-03-16 07:10:03
这是最好的答案。你是最棒的
2021-03-22 07:10:03
@velkoon,它与 jQuery无关,但它是纯 javascript。结构体或模式称为自调用函数next参数是实际声明,如果你仔细看,第二个function()就是注入作为next由函数本身。在任何需要在其他内容之后执行某些内容的地方,您可以复制粘贴前 4 行,然后简单地将要执行的代码放在两个{...}文字之间
2021-04-06 07:10:03
这对我有用,是的。好点,我想知道为什么没有人提到这一点。并非所有时间都想使用同步/异步
2021-04-09 07:10:03
我不熟悉 JQuery(我认为这是使用的,基于 jsfiddle 演示中的设置)并且对它的工作原理感到非常困惑。有人可以解释为什么函数没有名称,以及为什么整个代码片段都在括号中?(function(next)...)另外,next当它从未被声明时如何工作?它是原生的 JQuery 函数吗?
2021-04-10 07:10:03