async / await ngầm trả về lời hứa?


111

Tôi đọc rằng các hàm không đồng bộ được đánh dấu bằng asynctừ khóa ngầm trả về một lời hứa:

async function getVal(){
 return await doSomethingAync();
}

var ret = getVal();
console.log(ret);

nhưng điều đó không nhất quán ... giả sử doSomethingAsync()trả về một lời hứa và từ khóa await sẽ trả về giá trị từ lời hứa, không phải itsef lời hứa, thì hàm getVal của tôi sẽ trả về giá trị đó, không phải là lời hứa ngầm.

Vậy trường hợp chính xác là gì? Các hàm được đánh dấu bởi từ khóa async có ngầm trả về các lời hứa hay chúng ta kiểm soát những gì chúng trả về?

Có lẽ nếu chúng ta không trả lại một cách rõ ràng một điều gì đó, thì họ đã ngầm trả lại một lời hứa…?

Nói rõ hơn, có sự khác biệt giữa phần trên và

function doSomethingAync(charlie) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(charlie || 'yikes');
        }, 100);
    })
}

async function getVal(){
   var val = await doSomethingAync();  // val is not a promise
   console.log(val); // logs 'yikes' or whatever
   return val;  // but this returns a promise
}

var ret = getVal();
console.log(ret);  //logs a promise

Trong phần tóm tắt của tôi, hành vi thực sự không phù hợp với các câu lệnh trả về truyền thống. Có vẻ như khi bạn trả về một giá trị không phải là lời hứa một cách rõ ràng từ một asynchàm, nó sẽ buộc gói nó trong một lời hứa. Tôi không có vấn đề gì lớn với nó, nhưng nó bất chấp JS bình thường.


1
Điều gì console.logcho thấy?
Barmar

đó là giá trị thông qua các chức năng hứa quyết tâm, chứ không phải hứa với bản thân
Alexander Mills

Có lẽ đang chờ đợi để mở ra kết quả từ lời hứa.
Hamlet Hakobyan

thực sự, tôi đã sai, nó ghi lại một lời hứa
Alexander Mills

2
Các hứa hẹn của JavaScript đang cố gắng bắt chước hành vi chờ đợi không đồng bộ của c #. Tuy nhiên, trong lịch sử đã có rất nhiều cấu trúc để hỗ trợ điều đó với c # và không có cấu trúc nào trong JavaScript. Vì vậy, mặc dù trong nhiều trường hợp sử dụng, nó có vẻ rất giống nhau, nhưng nó có phần nhầm lẫn.
Travis J

Câu trả lời:


137

Giá trị trả về sẽ luôn là một lời hứa. Nếu bạn không trả lại một lời hứa một cách rõ ràng, giá trị bạn trả về sẽ tự động được bao bọc trong một lời hứa.

async function increment(num) {
  return num + 1;
}

// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));

Điều tương tự ngay cả khi có một await.

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function incrementTwice(num) {
  const numPlus1 = await defer(() => num + 1);
  return numPlus1 + 1;
}

// Logs: 5
incrementTwice(3).then(num => console.log(num));

Hứa hẹn tự động mở, vì vậy nếu bạn trả về một lời hứa cho một giá trị từ bên trong một asynchàm, bạn sẽ nhận được một lời hứa cho giá trị (không phải một lời hứa cho một lời hứa cho giá trị).

function defer(callback) {
  return new Promise(function(resolve) {
    setTimeout(function() {
      resolve(callback());
    }, 1000);
  });
}

async function increment(num) {
  // It doesn't matter whether you put an `await` here.
  return defer(() => num + 1);
}

// Logs: 4
increment(3).then(num => console.log(num));

Trong phần tóm tắt của tôi, hành vi thực sự không phù hợp với các câu lệnh trả về truyền thống. Có vẻ như khi bạn trả về một giá trị không phải là lời hứa một cách rõ ràng từ một hàm không đồng bộ, nó sẽ buộc bọc nó trong một lời hứa. Tôi không có vấn đề gì lớn với nó, nhưng nó bất chấp JS bình thường.

ES6 có các hàm không trả về chính xác cùng giá trị với return. Các chức năng này được gọi là máy phát điện.

function* foo() {
  return 'test';
}

// Logs an object.
console.log(foo());

// Logs 'test'.
console.log(foo().next().value);

3
"giá trị bạn trả về sẽ tự động được bao bọc trong một lời hứa" bởi phương thức tĩnh Promise.resolve, tức là, nếu câu lệnh trả về của một hàm async là - return x; nó mặc nhiên trở thành - return Promise.resolve (x);
adnan2nd

Việc chỉ trả lại lời hứa được tạo tự động thay vì tự mình tạo nó có bị coi là hành vi xấu không? Bằng cách nào đó, tôi thích cách tiếp cận sạch sẽ trong nhiều trường hợp.
marlar

24

Tôi đã xem qua thông số kỹ thuật và tìm thấy thông tin sau. Phiên bản ngắn gọn là một bộ giải async functionmã cho một máy phát điện tạo ra Promises. Vì vậy, có, các hàm không đồng bộ trả về các lời hứa .

Theo thông số kỹ thuật tc39 , điều sau là đúng:

async function <name>?<argumentlist><body>

Desugars đến:

function <name>?<argumentlist>{ return spawn(function*() <body>, this); }

Trong đó spawn"là lệnh gọi đến thuật toán sau":

function spawn(genF, self) {
    return new Promise(function(resolve, reject) {
        var gen = genF.call(self);
        function step(nextF) {
            var next;
            try {
                next = nextF();
            } catch(e) {
                // finished with failure, reject the promise
                reject(e);
                return;
            }
            if(next.done) {
                // finished with success, resolve the promise
                resolve(next.value);
                return;
            }
            // not finished, chain off the yielded promise and `step` again
            Promise.resolve(next.value).then(function(v) {
                step(function() { return gen.next(v); });
            }, function(e) {
                step(function() { return gen.throw(e); });
            });
        }
        step(function() { return gen.next(undefined); });
    });
}

"Phiên bản ngắn gọn là một hàm không đồng bộ giải mã cho một trình tạo ra Hứa hẹn." Tôi nghĩ rằng bạn có thể nhầm lẫn async functionvới async function*. Trước đây chỉ đơn giản là trả lại một lời hứa. Sau đó trả về một trình tạo ra các lời hứa.
cdhowie

Câu trả lời này phần lớn là tham chiếu đến thông số kỹ thuật và sau khi xem xét, tôi không tin rằng có bất kỳ sự nhầm lẫn nào. Đúng là, các hàm không đồng bộ trả về các lời hứa, nhưng để làm điều này, chúng gỡ bỏ kết nối với các bộ tạo mang lại các lời hứa.
Jon Surrell

-1

Chỉ cần thêm await trước hàm của bạn khi bạn gọi nó:

var ret = await  getVal();
console.log(ret);

1
chờ đợi chỉ có hiệu lực trong chức năng không đồng bộ
Han Van Pham

-3

async không trả về lời hứa, từ khóa await đang chờ giải quyết lời hứa. async là một chức năng trình tạo nâng cao và chờ đợi hoạt động giống như năng suất

Tôi nghĩ rằng cú pháp (tôi không chắc chắn 100%) là

async function* getVal() {...}

Các chức năng của trình tạo ES2016 hoạt động giống như thế này. Tôi đã tạo một trình xử lý cơ sở dữ liệu dựa trên sự tẻ nhạt mà bạn lập trình như thế này

db.exec(function*(connection) {
  if (params.passwd1 === '') {
    let sql = 'UPDATE People SET UserName = @username WHERE ClinicianID = @clinicianid';
    let request = connection.request(sql);
    request.addParameter('username',db.TYPES.VarChar,params.username);
    request.addParameter('clinicianid',db.TYPES.Int,uid);
    yield connection.execSql();
  } else {
    if (!/^\S{4,}$/.test(params.passwd1)) {
      response.end(JSON.stringify(
        {status: false, passwd1: false,passwd2: true}
      ));
      return;
    }
    let request = connection.request('SetPassword');
    request.addParameter('userID',db.TYPES.Int,uid);
    request.addParameter('username',db.TYPES.NVarChar,params.username);
    request.addParameter('password',db.TYPES.VarChar,params.passwd1);
    yield connection.callProcedure();
  }
  response.end(JSON.stringify({status: true}));

}).catch(err => {
  logger('database',err.message);
  response.end(JSON.stringify({status: false,passwd1: false,passwd2: false}));
});

Lưu ý cách tôi chỉ lập trình nó giống như đồng bộ bình thường, đặc biệt tại

yield connection.execSql và tại yield connection.callProcedure

Hàm db.exec là một trình tạo dựa trên Promise khá điển hình

exec(generator) {
  var self = this;
  var it;
  return new Promise((accept,reject) => {
    var myConnection;
    var onResult = lastPromiseResult => {
      var obj = it.next(lastPromiseResult);
      if (!obj.done) {
        obj.value.then(onResult,reject);
      } else {
       if (myConnection) {
          myConnection.release();
        }
        accept(obj.value);
      }
    };
    self._connection().then(connection => {
      myConnection = connection;
      it = generator(connection); //This passes it into the generator
      onResult();  //starts the generator
    }).catch(error => {
      reject(error);
    });
  });
}

4
" async là một chức năng máy phát điện nâng cao " - không, thực sự là không.
Bergi

Như đã nêu ở trên - 'các hàm không đồng bộ' thực sự trả về một Lời hứa. Ít nhất về mặt khái niệm, điểm chính của câu lệnh 'async' là gói các giá trị trả về của hàm đó trong một lời hứa. Bạn thậm chí có thể 'chờ đợi' trên một hàm cũ đơn giản trả về Promise và tất cả đều hoạt động, bởi vì 'async function' === 'function trả về Promise'.
speechchter

2
@bergi, thực ra, nó là một chức năng máy phát điện nâng cao. một hàm tạo luôn trả về một lời hứa .. hoặc một cái gì đó.
Alexander Mills
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.