await działa tylko w funkcji asynchronicznej

130

Napisałem ten kod w lib/helper.js

var myfunction = async function(x,y) {
   ....
   reutrn [variableA, variableB]
}
exports.myfunction = myfunction;

a potem próbowałem użyć go w innym pliku

 var helper = require('./helper.js');   
 var start = function(a,b){
     ....
     const result = await helper.myfunction('test','test');
 }
 exports.start = start;

Wystąpił błąd

„await działa tylko w funkcji asynchronicznej”

Jaki jest problem?

j.doe
źródło
1
Cóż, problem polega na tym, że awaitmożna go używać tylko wewnątrz asyncfunkcji. Oznacza to, awaitże funkcja jest asynchroniczna, więc musi być zadeklarowana jako taka.
Pointy
Jaki jest obecny błąd?
acdcjunior
nadal to samo, SyntaxError:
await
Musisz udostępnić więcej kontekstu na temat swojego kodu.
Ele

Odpowiedzi:

161

Błąd nie odnosi się do, myfunctionale do start.

async function start() {
   ....

   const result = await helper.myfunction('test', 'test');
}

// My function
const myfunction = async function(x, y) {
  return [
    x,
    y,
  ];
}

// Start function
const start = async function(a, b) {
  const result = await myfunction('test', 'test');
  
  console.log(result);
}

// Call start
start();



Używam okazji tej kwestii doradzić o znanej strukturze z wykorzystaniem anty awaitktóry jest: return await.


ŹLE

async function myfunction() {
  console.log('Inside of myfunction');
}

// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later

// useless async here
async function start() {
  // useless await here
  return await myfunction();
}

// Call start
(async() => {
  console.log('before start');

  await start();
  
  console.log('after start');
})();


POPRAWNY

async function myfunction() {
  console.log('Inside of myfunction');
}

// Here we wait for the myfunction to finish
// and then returns a promise that'll be waited for aswell
// It's useless to wait the myfunction to finish before to return
// we can simply returns a promise that will be resolved later

// Also point that we don't use async keyword on the function because
// we can simply returns the promise returned by myfunction
function start() {
  return myfunction();
}

// Call start
(async() => {
  console.log('before start');

  await start();
  
  console.log('after start');
})();


Pamiętaj też, że istnieje specjalny przypadek, w którym return awaitjest poprawny i ważny: (używając try / catch)

Czy istnieją problemy z wydajnością w przypadku polecenia „return await”?

Grégory NEUT
źródło
Ale to nie działa, zaktualizowałem kod. Nadal
pojawia
@ j.doe Dodałem fragment
Grégory NEUT
2
Dzięki, znalazłem swój problem. Próbowałem to zrobić wewnątrz wywołania zwrotnego, czyli funkcji start (). Rozwiązaniem było: const start = async function (a, b) {task.get (options, async function (error, result1) {const result = await myfunction ('test', 'test');
j.doe
Biorąc pod uwagę, że Node jest jednowątkowy. Czy nie zmniejsza liczby żądań na minuty, a także zwiększa opóźnienie między żądaniami pełnego wypełnienia.
Rishabh Dhiman
1
Warto wspomnieć, że w "PRAWIDŁOWYM" przykładzie nie jest konieczne deklarowanie startjako asyncfunkcji (chociaż niektórzy i tak zdecydują się to zrobić, aby być bardziej jednoznacznym)
Gershom
11

Kiedy pojawił się ten błąd, okazało się, że wywołałem funkcję map w mojej funkcji „async”, więc ten komunikat o błędzie w rzeczywistości odnosi się do funkcji mapy, która nie została oznaczona jako „async”. Omówiłem ten problem, usuwając wywołanie „await” z funkcji mapy i wymyślając inny sposób uzyskania oczekiwanego zachowania.

var myfunction = async function(x,y) {
    ....
    someArray.map(someVariable => { // <- This was the function giving the error
        return await someFunction(someVariable);
    });
}
John Langford
źródło
2
To był dla mnie problem. Zamieniłem funkcję mapy na pętlę for, co było dla mnie łatwym rozwiązaniem. Jednak to rozwiązanie może nie działać dla Ciebie w zależności od Twojego kodu.
Thomas
6
Do Twojej wiadomości możesz też to zrobićsomeArray.map(async (someVariable) => { return await someFunction(someVariable)})
ptim
1
Znak awaitw Twoim kodzie wprowadza w błąd, ponieważ Array.mapnie będzie obsługiwał funkcji jako funkcji asynchronicznej. Aby było jasne, po zakończeniu mapfunkcji someFunctionwszystko będzie w toku. Jeśli naprawdę chcesz poczekać, aż funkcje się zakończą, musisz napisać: await Promise.all(someArray.map(someVariable => someFunction(someVariable)))lub await Promise.all(someArray.map(someFunction))).
Grégory NEUT
9

Aby użyć await, kontekst wykonywania musi mieć asynccharakter

Jak zostało powiedziane, musisz najpierw zdefiniować naturę swojego executing contextmiejsca, w którym jesteś gotów wykonać awaitzadanie.

Wystarczy wstawić asyncprzed fndeklaracją, w której Twoje asynczadanie zostanie wykonane.

var start = async function(a, b) { 
  // Your async task will execute with await
  await foo()
  console.log('I will execute after foo get either resolved/rejected')
}

Wyjaśnienie:

W swoim pytaniu importujesz to, methodco ma asynchronousnaturę i będzie wykonywane równolegle. Ale miejsce, w którym próbujesz wykonać tę asyncmetodę, znajduje się wewnątrz innej, execution contextktórą musisz zdefiniować, asyncaby użyć await.

 var helper = require('./helper.js');   
 var start = async function(a,b){
     ....
     const result = await helper.myfunction('test','test');
 }
 exports.start = start;

Zastanawiasz się, co się dzieje pod maską

awaitzużywa metody / funkcje zwracające obietnicę / przyszłość / zadania i asyncoznacza metodę / funkcję jako zdolną do używania await.

Również, jeśli jesteś zaznajomiony promises, awaitfaktycznie robi ten sam proces obietnicy / postanowienia. Tworzenie łańcucha obietnic i wykonywanie następnego zadania w resolveoddzwonieniu.

Więcej informacji można znaleźć w MDN DOCS .

Satyam Pathak
źródło
Nawet przy async w funkcji start
pojawia się
Nie jestem pewien, gdzie brakuje i otrzymujesz ten błąd, nie ma tak złożonego wyjaśnienia, aby rozwiązać ten błąd.
Satyam Pathak
to jest prawidłowa odpowiedź i faktycznie wyjaśnia powód podkreślenia. głosował.
linehrr
3

Bieżąca implementacja async/ awaitobsługuje tylko awaitsłowo kluczowe wewnątrz asyncfunkcji Zmień startsygnaturę funkcji, aby można było używać jej awaitwewnątrz start.

 var start = async function(a, b) {

 }

Dla zainteresowanych propozycja na najwyższy poziom awaitjest obecnie na etapie 2: https://github.com/tc39/proposal-top-level-await

user835611
źródło
1
Niestety, w zasadzie oznacza to, że WSZYSTKIE funkcje będą musiały być asynchroniczne w całej bazie kodu. Ponieważ jeśli chcesz użyć await, musisz to zrobić w funkcji asynchronicznej, co oznacza, że ​​musisz poczekać na odpowiedź tej funkcji w funkcji ją wywołującej - znowu oznacza to, że WSZYSTKIE twoje funkcje będą musiały stać się asynchroniczne. Dla mnie oznacza to, że await async nie jest gotowy do użycia. Jeśli możesz użyć await do wywołania metody asynchronicznej, niezależnie od tego, czy bieżąca funkcja jest synchroniczna, czy asynchroniczna, będzie ona gotowa na czas główny.
Rodney P. Barbati
1
Każda funkcja, która jest za pośrednictwem dowolnego poziomu zadnie uzależnione od wyników zewnętrznego wolno przetwarzać i powinno się być zdefiniowana async- to cały punkt z async.
Gershom
Możesz go obecnie używać w replikacji węzła za pomocą --experimental-repl-awaitopcji.
Łódź
3

Miałem ten sam problem i następujący blok kodu podawał ten sam komunikat o błędzie:

repositories.forEach( repo => {
        const commits = await getCommits(repo);
        displayCommit(commits);
});

Problem polega na tym, że metoda getCommits () była asynchroniczna, ale przekazałem jej argument repo, który również został utworzony przez Promise. Musiałem więc dodać do niego słowo async w ten sposób: async (repo) i zaczęło działać:

repositories.forEach( async(repo) => {
        const commits = await getCommits(repo);
        displayCommit(commits);
});
Emil
źródło
0

async / await to mechanizm obsługi obietnicy, możemy to zrobić na dwa sposoby

functionWhichReturnsPromise()
            .then(result => {
                console.log(result);
            })
            .cathc(err => {
                console.log(result);

            });

lub możemy użyć await, aby najpierw poczekać na pełne wypełnienie obietnicy, co oznacza, że ​​zostanie odrzucona lub rozwiązana.

Teraz, jeśli chcemy użyć await (oczekiwania na spełnienie obietnicy) wewnątrz funkcji, obowiązkowe jest, aby funkcja kontenera była funkcją asynchroniczną, ponieważ czekamy na asynchroniczne spełnienie obietnicy || ma sens, prawda ?.

async function getRecipesAw(){
            const IDs = await getIds; // returns promise
            const recipe = await getRecipe(IDs[2]); // returns promise
            return recipe; // returning a promise
        }

        getRecipesAw().then(result=>{
            console.log(result);
        }).catch(error=>{
            console.log(error);
        });
Lord
źródło
-2

„await działa tylko w funkcji asynchronicznej”

Ale dlaczego? „await” jawnie zamienia wywołanie asynchroniczne w wywołanie synchroniczne, a zatem obiekt wywołujący nie może być asynchroniczny (lub asynchroniczny) - przynajmniej nie dlatego, że wywołanie jest wykonywane w trybie „await”.

mn_test347
źródło
1
Właściwie, await nie czeka na wyniki - natychmiast zwraca obietnicę. Dokładnie to chciałem przekazać. Jeśli await faktycznie czekał i nie zwracał kontroli do obiektu wywołującego, wówczas żadna funkcja, która zawiera słowo kluczowe await, nie mogłaby być oznaczona jako asynchroniczna. Ale zamiast tego mamy dowolną funkcję, która zawiera await lub wywołuje funkcję, która ostatecznie wywołuje funkcję zawierającą await, musi być async. Zasadniczo, jeśli wywołasz czekaj jeszcze raz - wszystkie funkcje muszą być oznaczone jako asynchroniczne.
Rodney P. Barbati
-5

Tak, await / async był świetnym pomysłem, ale implementacja jest całkowicie zepsuta.

Z jakiegoś powodu słowo kluczowe await zostało zaimplementowane w taki sposób, że może być używane tylko w ramach metody asynchronicznej. W rzeczywistości jest to błąd, chociaż nie zobaczysz go jako takiego nigdzie poza tym tutaj. Rozwiązaniem tego błędu byłoby zaimplementowanie słowa kluczowego await w taki sposób, że może być używane tylko DO WYWOŁANIA funkcji asynchronicznej, niezależnie od tego, czy funkcja wywołująca jest sama w sobie synchroniczna, czy asynchroniczna.

Z powodu tego błędu, jeśli używasz await do wywołania prawdziwej funkcji asynchronicznej gdzieś w kodzie, WSZYSTKIE twoje funkcje muszą być oznaczone jako async, a WSZYSTKIE wywołania funkcji muszą używać await.

Zasadniczo oznacza to, że musisz dodać narzut obietnic do wszystkich funkcji w całej aplikacji, z których większość nie jest i nigdy nie będzie asynchroniczna.

Jeśli naprawdę się nad tym zastanowisz, użycie await w funkcji powinno wymagać, aby funkcja zawierająca słowo kluczowe await NIE BYŁA ASYNC - dzieje się tak, ponieważ słowo kluczowe await wstrzyma przetwarzanie w funkcji, w której znajduje się słowo kluczowe await. Jeśli przetwarzanie w tej funkcji jest wstrzymane, to zdecydowanie NIE jest asynchroniczne.

Tak więc, dla twórców javascript i ECMAScript - napraw implementację await / async w następujący sposób ...

  • await można używać tylko do wywoływania funkcji asynchronicznych.
  • await może pojawić się w dowolnej funkcji, synchronicznej lub asynchronicznej.
  • Zmień komunikat o błędzie z „await działa tylko w funkcji asynchronicznej” na „await może być używany tylko do wywoływania funkcji asynchronicznych”.
Rodney P. Barbati
źródło
Możesz to nazwać błędem, jeśli chcesz, ale nie zgadzam się. Nie ma czegoś takiego jak kod, który „zatrzymuje się” - raczej istnieje kod, który nie może zakończyć się bez wyników jakiegoś zewnętrznego procesu (zwykle io). Taki kod należy nazwać „asynchronicznym”, ponieważ wiele procesów zewnętrznych powinno być w stanie działać w tym samym czasie (niesynchronicznie), w przeciwieństwie do maszyny wirtualnej javascript, która jest jednowątkowa. Jeśli masz wiele funkcji, które wymagają refaktoryzacji async, odzwierciedla to fakt, że wiele z nich wymaga wyników procesów zewnętrznych. Moim zdaniem jest to całkowicie kanoniczne.
Gershom
Warto również wspomnieć o strasznej wadzie polegającej na ograniczaniu awaitmożliwości korzystania tylko z wywołań funkcji: w przypadku pojedynczego procesu zewnętrznego tylko jeden punkt w kodzie javascript może zostać powiadomiony o zakończeniu tego procesu. Na przykład, jeśli zawartość pliku jest potrzebna do 3 niezależnych celów, każdy cel musiałby robić niezależnie let content = await readTheFile();- dzieje się tak, ponieważ nie można czekać na „obietnicę zawartości pliku”, a jedynie „czynność czytania pliku i wznowienie, gdy zostanie czytać".
Gershom
Ok, nie nazywajmy tego kodem, który zatrzymuje się lub kodem, którego nie można zakończyć, ale co powiesz na zablokowane oczekiwanie. Oto rub - funkcja, która jest zablokowana jako oczekiwanie lub której nie można ukończyć, to funkcja zawierająca słowo kluczowe await. To nie funkcja asynchroniczna jest wywoływana za pomocą słowa kluczowego await. Dlatego funkcja zawierająca słowo kluczowe await zdecydowanie NIE powinna być oznaczana jako async - jest blokowana w oczekiwaniu, co jest przeciwieństwem asynchronicznej.
Rodney P. Barbati
Aby to całkowicie wyjaśnić, rozważmy, co następuje - await ma na celu uproszczenie korzystania z funkcji asynchronicznych, sprawiając, że wydają się być synchroniczne (tj. Pozwala mi robić rzeczy w określonej kolejności). Wymuszenie asynchronicznej funkcji zawierającej await jest kompletnym błędem - użyłeś await, aby stała się synchroniczna. Funkcja zawierająca await jest absolutnie, w każdy możliwy sposób, NIE jest funkcją asynchroniczną !!!
Rodney P. Barbati
1
@Gershom - to brzmi rozsądnie. Dzięki!
Rodney P. Barbati