async / รอผลตอบแทนสัญญาโดยปริยาย?


111

ฉันอ่านว่าฟังก์ชัน async ที่ทำเครื่องหมายโดยasyncคำหลักส่งคืนสัญญาโดยปริยาย:

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

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

แต่นั่นไม่สอดคล้องกัน ... สมมติว่าdoSomethingAsync()ส่งคืนคำสัญญาและคีย์เวิร์ด await จะคืนค่าจากสัญญาไม่ใช่คำสัญญาจากนั้นฟังก์ชัน getVal ของฉันควรส่งคืนค่านั้นไม่ใช่คำสัญญาโดยปริยาย

แล้วกรณีนี้คืออะไรกันแน่? ฟังก์ชันที่ทำเครื่องหมายโดยคำหลัก async ส่งคืนสัญญาโดยปริยายหรือเราควบคุมสิ่งที่ส่งคืนหรือไม่

บางทีถ้าเราไม่คืนของอย่างชัดเจนพวกเขาก็จะคืนสัญญาโดยปริยาย ... ?

เพื่อให้ชัดเจนยิ่งขึ้นมีความแตกต่างระหว่างด้านบนและ

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

ในบทสรุปของฉันพฤติกรรมนั้นไม่สอดคล้องกับงบตอบแทนแบบดั้งเดิม ดูเหมือนว่าเมื่อคุณส่งคืนค่าที่ไม่ใช่สัญญาอย่างชัดเจนจากasyncฟังก์ชันฟังก์ชันนั้นจะบังคับให้รวมไว้ในสัญญา ฉันไม่มีปัญหาใหญ่กับมัน แต่มันต่อต้าน JS ปกติ


1
สิ่งที่ไม่console.logแสดง?
Barmar

เป็นค่าที่ส่งผ่านโดยฟังก์ชันแก้ไขคำสัญญาไม่ใช่คำสัญญา
Alexander Mills

บางทีอาจรอการแกะผลจากคำสัญญา
Hamlet Hakobyan

ที่จริงฉันผิดมันบันทึกคำสัญญา
Alexander Mills

2
คำสัญญาของ JavaScript พยายามเลียนแบบ async ของ c # กำลังรอพฤติกรรม อย่างไรก็ตามในอดีตมีโครงสร้างจำนวนมากที่รองรับ c # และไม่มีใน JavaScript ดังนั้นในกรณีการใช้งานหลาย ๆ กรณีอาจดูคล้ายกันมาก แต่ก็เป็นการเรียกชื่อผิดอยู่บ้าง
Travis J

คำตอบ:


138

มูลค่าที่กลับมาจะเป็นสัญญาเสมอ หากคุณไม่คืนคำสัญญาอย่างชัดเจนมูลค่าที่คุณส่งคืนจะรวมอยู่ในสัญญาโดยอัตโนมัติ

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));

แม้ว่าจะมี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));

สัญญาว่าจะแกะอัตโนมัติดังนั้นหากคุณส่งคืนสัญญาสำหรับค่าจากภายในasyncฟังก์ชันคุณจะได้รับสัญญาสำหรับมูลค่า (ไม่ใช่คำสัญญาสำหรับคำมั่นสัญญาสำหรับมูลค่า)

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));

ในบทสรุปของฉันพฤติกรรมนั้นไม่สอดคล้องกับงบตอบแทนแบบดั้งเดิม ดูเหมือนว่าเมื่อคุณส่งคืนค่าที่ไม่ใช่สัญญาอย่างชัดเจนจากฟังก์ชัน async มันจะบังคับให้ห่อด้วยสัญญา ฉันไม่มีปัญหาใหญ่กับมัน แต่มันต่อต้าน JS ปกติ

ES6 มีฟังก์ชันที่ไม่ส่งกลับค่าเดียวกับreturn. ฟังก์ชันเหล่านี้เรียกว่าเครื่องกำเนิดไฟฟ้า

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

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

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

3
"ค่าที่คุณส่งคืนจะถูกรวมไว้ในสัญญาโดยอัตโนมัติ" โดยวิธีคงที่ Promise.resolve กล่าวคือถ้าคำสั่ง return ของฟังก์ชัน async คือ - return x; โดยปริยายจะกลายเป็น - ส่งคืน Promise.resolve (x);
adnan2nd

ถือเป็นการปฏิบัติที่ไม่ดีในการส่งคืนสัญญาที่สร้างขึ้นโดยอัตโนมัติแทนที่จะสร้างขึ้นเองอย่างชัดเจนหรือไม่? ฉันชอบแนวทางที่สะอาดในหลาย ๆ กรณี
marlar

24

ฉันดูข้อมูลจำเพาะและพบข้อมูลต่อไปนี้ เวอร์ชันสั้น ๆ คือasync functiondesugars ไปยังเครื่องกำเนิดไฟฟ้าที่ให้ผลPromises ดังนั้นใช่ฟังก์ชั่น async กลับสัญญา

ตามข้อมูลจำเพาะ tc39ต่อไปนี้เป็นจริง:

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

Desugars ไปที่:

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

โดยที่spawn"เป็นการเรียกไปยังอัลกอริทึมต่อไปนี้":

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); });
    });
}

"เวอร์ชันสั้น ๆ คือฟังก์ชัน async จะแยกไปยังเครื่องกำเนิดไฟฟ้าที่ให้สัญญา" ฉันคิดว่าคุณอาจจะทำให้เกิดความสับสนกับasync function async function*อดีตเพียงแค่ตอบแทนคำสัญญา หลังส่งคืนเครื่องกำเนิดไฟฟ้าที่ให้สัญญา
cdhowie

คำตอบนี้ส่วนใหญ่อ้างอิงถึงข้อมูลจำเพาะและหลังจากการตรวจสอบฉันไม่เชื่อว่าจะมีความสับสนใด ๆ เป็นความจริงฟังก์ชัน async จะส่งคืนคำสัญญา แต่เพื่อที่จะทำสิ่งนี้พวกเขาจึงขอให้กำเนิดที่ให้คำมั่นสัญญา
Jon Surrell

-1

เพียงแค่เพิ่มการรอก่อนฟังก์ชันของคุณเมื่อคุณเรียกมัน:

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

1
await ใช้ได้เฉพาะในฟังก์ชัน async
Han Van Pham

-3

async ไม่คืนคำสัญญาคีย์เวิร์ดรอคอยการแก้ปัญหาของสัญญา async เป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่ได้รับการปรับปรุงและรอคอยผลตอบแทนเล็กน้อย

ฉันคิดว่าไวยากรณ์ (ฉันไม่แน่ใจ 100%) คือ

async function* getVal() {...}

ฟังก์ชันเครื่องกำเนิดไฟฟ้า ES2016 ทำงานได้ในลักษณะนี้ ฉันได้สร้างตัวจัดการฐานข้อมูลขึ้นอยู่กับความน่าเบื่อที่คุณตั้งโปรแกรมไว้เช่นนี้

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}));
});

สังเกตว่าฉันแค่ตั้งโปรแกรมให้เหมือนกับซิงโครนัสปกติโดยเฉพาะที่

yield connection.execSql และที่ yield connection.callProcedure

ฟังก์ชัน db.exec เป็นเครื่องกำเนิดไฟฟ้าตาม Promise ที่ค่อนข้างธรรมดา

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 เป็นฟังก์ชันเครื่องกำเนิดไฟฟ้าขั้นสูง " - ไม่มันไม่ใช่
Bergi

ตามที่ระบุไว้ข้างต้น - 'async functions' จะส่งคืนสัญญาอย่างแท้จริง ตามแนวคิดอย่างน้อยประเด็นหลักของคำสั่ง 'async' คือการรวมค่าส่งคืนของฟังก์ชันนั้นไว้ในสัญญา คุณยังสามารถ 'รอ' ในฟังก์ชันเก่าธรรมดาที่ส่งคืน Promise และทุกอย่างก็ใช้งานได้เพราะ 'async function' === 'function return Promise'
Spechter

2
@bergi จริงๆแล้วมันเป็นฟังก์ชั่นเครื่องกำเนิดไฟฟ้าขั้นสูง ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่ส่งคืนสัญญา.. หรือบางสิ่งบางอย่างเสมอ
Alexander Mills
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.