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

DA.*_*DA. 144 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
};
Run Code Online (Sandbox Code Playgroud)

我提出了这个解决方案,但不知道这是否是一个聪明的方法.

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
        };
    }
};
Run Code Online (Sandbox Code Playgroud)

这是合法的吗?是否有更优雅的方式来处理它?也许用jQuery?

Mat*_*Way 118

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

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!');
    });    
}
Run Code Online (Sandbox Code Playgroud)

根据@Janaka Pushpakumara的建议,您现在可以使用箭头功能来实现相同的功能.例如:

firstFunction(() => console.log('huzzah, I\'m done!'))

  • @zerkms - 精心制作? (9认同)
  • 回调不方便处理异步,承诺更容易和灵活 (7认同)
  • 它变成了offtopic,但我个人认为这些天没有理由更喜欢回调:-)不记得我过去2年写的任何代码都提供了对promises的回调. (3认同)
  • 由于我最近使用了node.js,我想我可能有点偏见...... (2认同)
  • 如果需要,可以在es6中使用箭头函数secondFunction(){firstFunction((response)=> {console.log(response);}); } (2认同)

nos*_*tio 47

看来你在这里忽略了一个重点: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
        };
    }
};
Run Code Online (Sandbox Code Playgroud)

你不必等待isPaused.当您看到"此处"警报时,isPaused将会false已经firstFunction返回.那是因为你不能从for循环(// do something)内部"屈服" ,循环可能不会被中断并且必须首先完全完成(更多细节:Javascript线程处理和竞争条件).

也就是说,您仍然可以使内部的代码流firstFunction为异步,并使用回调或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);
    });
}
Run Code Online (Sandbox Code Playgroud)

另外,JavaScript 1.7引入了yield关键字作为生成器的一部分.这将允许在其他同步JavaScript代码流中"打击"异步漏洞(更多细节和示例).但是,浏览器对生成器的支持目前仅限于Firefox和Chrome,AFAIK.

  • 你救了我.$ .Deferred()就是我一直在努力的目标.谢谢 (3认同)

Faw*_*waz 38

使用async/await:

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};
Run Code Online (Sandbox Code Playgroud)

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

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

  • 对于那些坚持支持较旧浏览器的人,IE不支持异步/等待 (5认同)
  • @Lewistrick只要记住“await”只能在“async”方法中使用。因此,如果您将父函数设置为“async”,您可以在内部调用任意数量的“async method”(带或不带“await”)。 (2认同)

dav*_*rad 13

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

(function(next) {
  //do something
  next()
}(function() {
  //do some more
}))
Run Code Online (Sandbox Code Playgroud)

仅仅为了盲目等待而使用超时是不好的做法;并涉及承诺只会增加代码的复杂性。在 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
}))
Run Code Online (Sandbox Code Playgroud)

一个小演示 -> http://jsfiddle.net/5jdeb93r/

  • 这对我有用,是的。好点,我想知道为什么没有人提到这一点。并非所有时候都想使用同步/异步 (2认同)

Jak*_*cki 7

等待一个功能首先完成的一种优雅方法是将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)
})
Run Code Online (Sandbox Code Playgroud)

首先,创建一个Promise。上面的功能将在2秒钟后完成。我用来setTimeout演示指令需要花费一些时间的情况(根据您的示例)。

async function secondFunction(){
    let result = await firstFunction
    console.log(result)
    console.log('next step')
}; 

secondFunction()
Run Code Online (Sandbox Code Playgroud)

对于第二个功能,您可以使用异步/等待功能,在继续执行说明之前,您将等待第一个功能完成。

注意:您可以简单地解决Promise,而无需像so这样的任何值resolve()。在我的示例中,我解决了Promise的值,y然后可以在第二个函数中使用该值。

希望它对仍然访问此线程的人有所帮助。


Mer*_*n K 6

可以使用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; 
})
Run Code Online (Sandbox Code Playgroud)

一个承诺可以有

已完成 - 操作成功完成

被拒绝 - 操作失败

待处理 - 两项操作均尚未完成

已解决 - 已履行或拒绝

  • 这对雅库布的回答没有什么帮助,而且帮助也很小。 (2认同)
  • 但当您提出请求时,并不能保证您能获得该值。 (2认同)

ker*_*mit 5

承诺的唯一问题是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
Run Code Online (Sandbox Code Playgroud)

这并不能满足您的要求。它立即执行功能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/zh-CN/docs/Web/JavaScript/事件循环

另外,与此主题相关的另一个类似的,评分很高的问题:我应该如何调用3个函数才能一个接一个地执行它们?

  • 对于那些拒绝投票的人,我很想知道这个答案出了什么问题。 (8认同)