简体   繁体   English

Javascript 等待异步中未按正确顺序执行 function

[英]Javascript Awaits not executing in correct order within Async function

I know their are plenty of questions regarding async and await and promises on here, Ive looked at all of them and I cant seem to make any of the solutions work with my specific situation.我知道他们在这里有很多关于 async 和 await 和 promises 的问题,我查看了所有这些问题,但我似乎无法让任何解决方案适用于我的具体情况。 Im sure it is because I am confused about the use of promises and callbacks.我确定这是因为我对 Promise 和回调的使用感到困惑。 All I want to do is fire these 3 api calls in order.我要做的就是按顺序触发这 3 个 api 调用。 This is my most recent attempt:-这是我最近的尝试:-

    async function getData() {
        var firstCall = api.getBaseInfo(name, num, (error, response) => {
            console.log(response)
        });
        var secondCall = api.getMainInfo(address, company, { type:'init' , family: name, id: num }, (error, response) => {
            console.log(response) 
        });
        var thirdCall =  api.getBackData(num, (error, orders, genre) => {
            console.log(orders)
        });
        await firstCall
        await secondCall
        await thirdCall
    }
    getData()

I have tried all sorts of variations incorporating.then, multiple async functions, normal functions but everything I do just has the output arrive as if all 3 calls are fired simultaneously.我尝试了各种变体,包括多个异步函数、普通函数,但我所做的一切只是让 output 到达,就好像所有 3 个调用同时触发一样。 Im not bothered what methods are used, I would just like to know how I can arrange my code so that it each call will execute after the previous has finished.我不介意使用什么方法,我只想知道如何安排我的代码,以便每次调用都将在前一个调用完成后执行。

From your description it appears that your functions don't return Promises.根据您的描述,您的函数似乎没有返回 Promises。 Instead they handle asynchronous behavior via callbacks (where you write your console.log).相反,它们通过回调(您在其中编写 console.log)处理异步行为。 Without promises async/await does nothing at all .没有 promises async/await什么都不做 The reason your code does not throw any errors is that await was designed to silently ignore anything that is not a promise in order to support functions that may or may not return a Promise (eg. in some situations the function returns a Promise but in others it returns null ) The reason your code does not throw any errors is that await was designed to silently ignore anything that is not a promise in order to support functions that may or may not return a Promise (eg. in some situations the function returns a Promise but in others它返回null

In order to execute your functions in sequence you need to wait for each function to complete.为了按顺序执行您的功能,您需要等待每个 function 完成。 And the one place we know they complete is inside their callbacks.我们知道他们完成的一个地方是在他们的回调中。 So you need to rewrite your code as follows:所以你需要重写你的代码如下:

function getData() {
    api.getBaseInfo(name, num, (error, response) => {
        console.log(response);

        api.getMainInfo(address, company, { type:'init' , family: name, id: num }, (error, response) => {
            console.log(response);

            api.getBackData(num, (error, orders, genre) => {
                console.log(orders)
            });
        });
    });
}
getData();

This way each function is allowed to complete before executing the next function.这样每个 function 都可以在执行下一个 function 之前完成。

Promises承诺

While the above code works, more complicated code will generate very deep nesting.虽然上面的代码有效,但更复杂的代码会产生非常深的嵌套。 The nesting can be solved by using named functions instead of anonymous functions.嵌套可以通过使用命名函数而不是匿名函数来解决。 In fact in traditional programming (eg. C++) this is encouraged - you should refactor your code so that each function is as short as needed:事实上,在传统编程(例如 C++)中,这是鼓励的——你应该重构你的代码,以便每个 function 尽可能短:

function baseInfoHandler(error, response) {
    console.log(response);
    const query = { type:'init' , family: name, id: num };

    api.getMainInfo(address, company, query, mainInfoHandler);
}

function mainInfoHandler(error, response) {
    console.log(response);

    api.getBackData(num, backDataHandler);
}

function backDataHandler(error, orders, genre) {
    console.log(orders);
}

function getData() {
    api.getBaseInfo(name, num, baseInfoHandler);
}
getData();

The above code flattens the nesting of callbacks.上面的代码扁平化了回调的嵌套。 Some people (especially old-timers) might argue that this code is better written than the one with anonymous functions.有些人(尤其是老前辈)可能会争辩说,这段代码比使用匿名函数的代码写得更好。 However there are downsides to doing this refactor.但是,进行此重构也有缺点。

The first downside is a very minor one and can basically be ignored: some people just don't like to declare additional functions or variables.第一个缺点是一个非常小的缺点,基本上可以忽略:有些人就是不喜欢声明额外的函数或变量。 Using anonymous functions means we don't need to think about function names.使用匿名函数意味着我们不需要考虑 function 名称。 But this is just a matter of taste and like I mentioned can be completely ignored.但这只是口味问题,就像我提到的那样可以完全忽略。

The second downside is more critical.第二个缺点更为关键。 Flattening the code like the above will not work if your code use closures.如果您的代码使用闭包,则无法像上面那样展平代码。 Or at least it is more difficult to implement if you depend on closures.或者至少如果您依赖闭包,则实施起来会更加困难。 As such we need a way to nest scopes (maintain closures) but still avoid the deep nesting like the first example I gave.因此,我们需要一种嵌套范围(维护闭包)的方法,但仍要避免像我给出的第一个示例那样的深度嵌套。

Along comes a design pattern .随之而来的是一种设计模式 A bunch of talented programmers started discussing on various forums and blog posts about encapsulating asynchronous callbacks in an object that you can return.一群才华横溢的程序员开始在各种论坛和博客文章中讨论如何将异步回调封装在可以返回的 object 中。 The caller of the function can then use said object to handle the asynchronous behavior in any way they like. function 的调用者然后可以使用所述 object 以他们喜欢的任何方式处理异步行为。 The name of this design pattern is called Promise and before javascript shipped with a default implementation of Promise there were several libraries that implemented this design pattern often with different features but are all compatible with each other in one way: they all have a .then() method that you can pass your callbacks to.此设计模式的名称称为Promise ,在 javascript 附带 Promise 的默认实现之前,有几个库通常具有不同的功能,但它们都以一种方式相互兼容.then()方法,您可以将回调传递给。

Note: this is all the Promise design pattern does.注意:这就是 Promise 设计模式所做的全部。 Instead of you writing a function that accepts a callback you write a function that returns a Promise.与其编写一个接受回调的 function,不如编写一个返回 Promise 的 function。 The Promise has a .then() method that accepts the callback. Promise 有一个接受回调的 .then .then()方法。

The code is still callback based but now we can make the code neater by flattening the callback nesting.代码仍然是基于回调的,但现在我们可以通过扁平化回调嵌套来使代码更整洁。 First we need to convert your functions to promises:首先,我们需要将您的函数转换为 Promise:

function promiseBaseInfo (name,num) {
    return new Promise((resolve, reject) => {
        api.getBaseInfo(name, num, (error, response) => {
            if (error) {
                reject(error);
            }
            else {
                resolve(response);
            }
        });
    });
}

function promiseMainInfo(address, company, query) {
    return new Promise((resolve, reject) => {
        api.getMainInfo(address, company, query, (error, response) => {
            if (error) {
                reject(error);
            }
            else {
                resolve(response);
            }
        });
    });
}

function promiseBackData(num) {
    return new Promise((resolve, reject) => {
        api.getBackData(num, (error, orders, genre) => {
            if (error) {
                reject(error);
            }
            else {
                resolve({
                    orders: orders,
                    genre: genre
                });
            }
        });
    });
}

Now that we have the Promisified versions of your functions we can use them like this:现在我们已经有了你的函数的 Promisified 版本,我们可以像这样使用它们:

function getData() {
    promiseBaseInfo(name, num)
        .then(response => {
            console.log(response);

            return promiseMainInfo(address, company, { type:'init' , family: name, id: num });
        })
        .then(response => {
            console.log(response);

            return promiseBackData(num);
        })
        .then(response => {
            console.log(response.orders);
        });
}
getData();

As you can see, the .then() are all at the same level of nesting.如您所见, .then()都处于同一嵌套级别。

async/await异步/等待

While Promises help a lot in simplifying asynchronous code they are still callback based.虽然 Promises 在简化异步代码方面有很大帮助,但它们仍然是基于回调的。 Additionally, doing things like looping through a bunch of asynchronous tasks are complicated because you need to write a kind of recursive function to do so (which puts us back in callback territory).此外,执行诸如循环一堆异步任务之类的事情很复杂,因为您需要编写一种递归 function 来执行此操作(这使我们回到了回调领域)。

ECMAScript version 6 introduced a new syntax to handle promises: the async and await keywords. ECMAScript 版本 6 引入了一种新的语法来处理 Promise: asyncawait关键字。

What it does is simply compile code written like this:它所做的只是编译这样编写的代码:

async function foo () {
    let x = await bar();
    console.log(x);
}

into this:进入这个:

function foo () {
    return bar().then(x => console.log(x));
}

Internally from the interpreter's point of view nothing new was added to Javascript.从解释器的角度来看,Javascript 在内部没有添加任何新内容。 These are still Promises.这些仍然是 Promise。 However the compiler now handles how to structure your code correctly into promises.然而,编译器现在处理如何将您的代码正确地构造成 Promise。

Using async/await we can further simplify your code to:使用async/await我们可以进一步简化您的代码:

async function getData() {
    let response1 = await promiseBaseInfo(name, num);
    console.log(response1);

    let response2 = await promiseMainInfo(address, company, { type:'init' , family: name, id: num });
    console.log(response2);

    let response3 = await promiseBackData(num);
    console.log(response3.orders);
}
getData();

Note that neither Promise nor async/await are necessary to make your code work the way you want.请注意, Promiseasync/await都不是使您的代码按照您想要的方式工作所必需的。 And they don't change the behavior of your code - the getData() function is still asynchronous (not synchronous) and will return before it logs all the responses.而且它们不会改变您的代码的行为 - getData() function 仍然是异步的(不是同步的),并且会在记录所有响应之前返回。 It just makes the code easier to read.它只是使代码更易于阅读。 However, behind the scenes there is still a callback being scheduled and executed at a later date only with async/await we let the compiler write the callbacks and we just write the code in linear style.然而,在幕后仍然有一个回调被调度并在稍后执行,仅使用async/await我们让编译器编写回调,我们只是以线性方式编写代码。 It is important to remember that even with async/await the code is still not synchronous .重要的是要记住,即使使用async/await代码仍然不是同步的。

Also note that we still cannot use your original api... functions.另请注意,我们仍然无法使用您原来的api...功能。 We need to convert them to their Promisified versions.我们需要将它们转换为它们的 Promisified 版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM