ฟังก์ชั่นนั้นหมายถึงอะไรใน JavaScript?


275

ฉันเห็นโค้ดที่มีลักษณะ:

myObj.doSome("task").then(function(env) {
    // logic
});

ในกรณีที่ไม่then()มาจากไหน?


8
อัปเดต: ฉันพบว่ามันเกี่ยวข้องกับ CommonJS ที่สัญญากับ API sitepen.com/blog/2010/01/19/…
Kay Pale

คำตอบ:


348

วิธีดั้งเดิมในการจัดการกับการโทรแบบอะซิงโครนัสใน JavaScript นั้นเป็นการโทรกลับ สมมติว่าเราต้องทำการติดต่อสามครั้งไปยังเซิร์ฟเวอร์เพื่อตั้งค่าแอปพลิเคชันของเรา ด้วยการเรียกกลับรหัสอาจมีลักษณะดังต่อไปนี้ (สมมติว่าฟังก์ชั่น xhrGET เพื่อโทรเซิร์ฟเวอร์):

// Fetch some server configuration
    xhrGET('/api/server-config', function(config) {
        // Fetch the user information, if he's logged in
        xhrGET('/api/' + config.USER_END_POINT, function(user) {
            // Fetch the items for the user
            xhrGET('/api/' + user.id + '/items', function(items) {
                // Actually display the items here
            });
        });
    });

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

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

Promise API ออกแบบมาเพื่อแก้ปัญหาการซ้อนและปัญหาการจัดการข้อผิดพลาด

Promise API เสนอสิ่งต่อไปนี้:

  1. แต่ละภารกิจแบบอะซิงโครนัสจะส่งคืนpromiseวัตถุ
  2. แต่ละpromiseวัตถุจะมีthenฟังก์ชั่นที่สามารถรับอาร์กิวเมนต์สองsuccess ตัวคือตัวจัดการและerrorตัวจัดการ
  3. ความสำเร็จหรือตัวจัดการข้อผิดพลาดในthenฟังก์ชันจะถูกเรียกเพียงครั้งเดียวหลังจากงานอะซิงโครนัสเสร็จสิ้น
  4. thenฟังก์ชั่นยังจะกลับมาpromiseเพื่อให้การผูกมัดหลายสาย
  5. ตัวจัดการแต่ละตัว (ความสำเร็จหรือข้อผิดพลาด) สามารถส่งคืน a valueซึ่งจะถูกส่งผ่านไปยังฟังก์ชันถัดไปargumentในรูปแบบห่วงโซ่ของpromises
  6. หากตัวจัดการส่งคืน a promise(สร้างคำร้องขอแบบอะซิงโครนัสอีกครั้ง) ตัวจัดการถัดไป (ความสำเร็จหรือข้อผิดพลาด) จะถูกเรียกหลังจากการร้องขอนั้นเสร็จสิ้น

ดังนั้นโค้ดตัวอย่างก่อนหน้านี้อาจแปลเป็นดังนี้: ใช้สัญญาและ$httpบริการ (ใน AngularJs):

$http.get('/api/server-config').then(
    function(configResponse) {
        return $http.get('/api/' + configResponse.data.USER_END_POINT);
    }
).then(
    function(userResponse) {
        return $http.get('/api/' + userResponse.data.id + '/items');
    }
).then(
    function(itemResponse) {
        // Display items here
    }, 
    function(error) {
        // Common error handling
    }
);

การเผยแพร่ความสำเร็จและข้อผิดพลาด

การผูกมัดสัญญาเป็นเทคนิคที่ทรงพลังมากที่ช่วยให้เราสามารถใช้งานได้หลายอย่างเช่นการให้บริการโทรออกเซิร์ฟเวอร์ทำการประมวลผลข้อมูลบางส่วนแล้วประมวลผลข้อมูลกลับไปยังคอนโทรลเลอร์ แต่เมื่อเราทำงานด้วย promiseโซ่มีบางสิ่งที่เราต้องจำไว้

พิจารณาpromiseห่วงโซ่สมมุติต่อไปนี้ด้วยสามสัญญา P1, P2 และ P3 แต่ละตัวpromiseมีตัวจัดการสำเร็จและตัวจัดการข้อผิดพลาดดังนั้น S1 และ E1 สำหรับ P1, S2 และ E2 สำหรับ P2, S3 และ E3 และ E3 สำหรับ P3:

xhrCall()
  .then(S1, E1) //P1
  .then(S2, E2) //P2
  .then(S3, E3) //P3

ในการไหลปกติของสิ่งต่าง ๆ ที่ไม่มีข้อผิดพลาดแอปพลิเคชันจะไหลผ่าน S1, S2 และสุดท้าย S3 แต่ในชีวิตจริงสิ่งต่าง ๆ ไม่เคยราบรื่นเช่นนั้น P1 อาจพบข้อผิดพลาดหรือ P2 อาจพบข้อผิดพลาดทำให้เกิด E1 หรือ E2

พิจารณากรณีต่อไปนี้:

•เราได้รับการตอบสนองที่ประสบความสำเร็จจากเซิร์ฟเวอร์ใน P1 แต่ข้อมูลที่ส่งคืนนั้นไม่ถูกต้องหรือไม่มีข้อมูลในเซิร์ฟเวอร์ (คิดว่าอาร์เรย์ว่างเปล่า) ในกรณีเช่นนี้สำหรับ P2 สัญญาถัดไปก็ควรทริกเกอร์ตัวจัดการข้อผิดพลาด E2

•เราได้รับข้อผิดพลาดสำหรับสัญญา P2 ทำให้เกิด E2 แต่ภายในตัวจัดการเรามีข้อมูลจากแคชเพื่อให้แน่ใจว่าแอปพลิเคชันสามารถโหลดได้ตามปกติ ในกรณีนั้นเราอาจต้องการให้แน่ใจว่าหลังจากเรียก E2, S3

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

หากเราต้องการเรียกตัวจัดการความสำเร็จสำหรับสัญญาครั้งต่อไปในโซ่เราก็สามารถคืนค่าจากความสำเร็จหรือตัวจัดการข้อผิดพลาด

ในทางกลับกันหากเราต้องการทริกเกอร์ตัวจัดการข้อผิดพลาดสำหรับคำสัญญาต่อไปในโซ่เราสามารถทำได้โดยใช้deferredวัตถุและเรียกreject()วิธีการของมัน

ตอนนี้วัตถุรอการตัดบัญชีคืออะไร?

วัตถุที่เลื่อนออกไปใน jQuery หมายถึงหน่วยของงานที่จะแล้วเสร็จในภายหลังโดยทั่วไปจะไม่ตรงกัน เมื่อหน่วยของงานเสร็จสมบูรณ์deferredวัตถุสามารถตั้งค่าให้แก้ไขหรือล้มเหลว

deferredวัตถุมีpromiseวัตถุ ผ่านpromiseวัตถุที่คุณสามารถระบุสิ่งที่จะเกิดขึ้นเมื่อหน่วยงานเสร็จสมบูรณ์ คุณทำได้โดยการตั้งค่าฟังก์ชั่นการโทรกลับบนpromiseวัตถุ

วัตถุที่ถูกเลื่อนเวลาใน Jquery: https://api.jquery.com/jquery.deferred/

วัตถุที่ถูกเลื่อนเวลาใน AngularJs: https://docs.angularjs.org/api/ng/service/ $ q


3
เขียนได้ดีมาก สิ่งนี้ได้ช่วยฉันตอกย้ำสัญญา
Ju66ernaut

ตัวจัดการข้อผิดพลาดพารามิเตอร์ตัวที่สองเป็นตัวเลือกเสมอหรือไม่
1.21 gigawatts

นี่คือคำตอบที่ดีที่สุดที่ฉันเคยเห็น!
อิหม่าม Bux

78

() ฟังก์ชั่นนั้นเกี่ยวข้องกับ "สัญญาจาวาสคริปต์" ที่ใช้ในห้องสมุดหรือกรอบบางอย่างเช่น jQuery หรือ AngularJS

คำมั่นสัญญาเป็นรูปแบบสำหรับการจัดการการดำเนินงานแบบอะซิงโครนัส คำสัญญาอนุญาตให้คุณเรียกวิธีการที่เรียกว่า "จากนั้น" ซึ่งช่วยให้คุณระบุฟังก์ชันที่จะใช้เป็นการเรียกกลับ

ดูข้อมูลเพิ่มเติมได้ที่: http://wildermuth.com/2013/8/3/JavaScript_Promises

และสำหรับสัญญาเชิงมุม: http://liamkaufman.com/blog/2013/09/09/using-angularjs-promises/


4
ดังนั้นมันจึงเหมือนกับการเรียกกลับที่ดำเนินการเมื่องานเสร็จ? มันแตกต่างกันอย่างไร
Muhammad Umer

3
JavaScript สัญญาในความคิดเห็นอื่น ๆ พูดว่า: A promise can only succeed or fail onceและIf a promise has succeeded or failed and you later add a success/failure callback, the correct callback will be called
เสี่ยว

นอกจากนี้นักเก็ต Promise ยังอธิบายถึงวิธีการใช้promiseและสิ่งที่จะต้องทำด้วยcallback
Xiao

ในหน้าแรกมีโค้ดที่หายไป (ช่องว่างสีขาวขนาดใหญ่) คนส่วนใหญ่จะคิดถึงการตรวจสอบองค์ประกอบและค้นหา URL ของซอที่อยู่ด้านล่าง ข้อความนี้มีไว้สำหรับส่วนที่เหลือ - ปริศนายังใช้ได้;)
DanteTheSmith

1
@MuhammadUmer: อ่านstackoverflow.com/a/31453579/1350476นี้(คำตอบโดย Sid)
SharpCoder

32

สำหรับความรู้ของฉันไม่มีthen()วิธีการในตัวjavascript(ในขณะที่เขียนนี้)

ปรากฏว่าสิ่งที่มันคือการที่จะกลับมามีวิธีการที่เรียกว่าdoSome("task")then

หากคุณบันทึกผลลัพธ์การส่งคืนของdoSome()คอนโซลคุณควรเห็นคุณสมบัติของสิ่งที่ถูกส่งคืน

console.log( myObj.doSome("task") ); // Expand the returned object in the
                                     //   console to see its properties.

ปรับปรุง (ณ ECMAScript6) : -

.then()ฟังก์ชั่นได้รับการรวมเพื่อจาวาสคริปต์บริสุทธิ์

จากเอกสาร Mozilla นี่ ,

เมธอด then () จะส่งคืนสัญญา มันต้องใช้สองข้อโต้แย้ง: ฟังก์ชั่นการโทรกลับสำหรับกรณีที่ประสบความสำเร็จและความล้มเหลวของสัญญา

ในทางกลับกันวัตถุสัญญานั้นถูกกำหนดให้เป็น

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

นั่นคือการPromiseกระทำเป็นตัวยึดตำแหน่งสำหรับค่าที่ยังไม่ได้คำนวณ แต่จะได้รับการแก้ไขในอนาคต และ.then()ฟังก์ชั่นนี้ใช้เพื่อเชื่อมโยงฟังก์ชั่นที่จะเรียกใช้บน Promise เมื่อแก้ไขแล้ว - ไม่ว่าจะเป็นความสำเร็จหรือความล้มเหลว


12
ตอนนั้นยังไม่มีบิวท์อิน.thenแต่คำสัญญาดั้งเดิมกำลังมาใน ES6: html5rocks.com/en/tutorials/es6/promises
janfoeh

ขอบคุณสำหรับคำตอบนี้ฉันคาดหวังว่าจะได้รับการติดต่อกลับที่ดี แต่มันกลับกลายเป็นฟังก์ชั่นจริงที่เรียกว่า 'แล้ว' ที่ถูกส่งคืน
spartikus

15

นี่คือสิ่งที่ฉันทำเพื่อตัวเองเพื่อเคลียร์ว่าสิ่งต่าง ๆ ทำงานอย่างไร ฉันเดาว่าคนอื่น ๆ ก็สามารถพบตัวอย่างที่เป็นประโยชน์นี้ได้:

doit().then(function() { log('Now finally done!') });
log('---- But notice where this ends up!');

// For pedagogical reasons I originally wrote the following doit()-function so that 
// it was clear that it is a promise. That way wasn't really a normal way to do 
// it though, and therefore Slikts edited my answer. I therefore now want to remind 
// you here that the return value of the following function is a promise, because 
// it is an async function (every async function returns a promise). 
async function doit() {
  log('Calling someTimeConsumingThing');
  await someTimeConsumingThing();
  log('Ready with someTimeConsumingThing');
}

function someTimeConsumingThing() {
  return new Promise(function(resolve,reject) {
    setTimeout(resolve, 2000);
  })
}

function log(txt) {
  document.getElementById('msg').innerHTML += txt + '<br>'
}
<div id='msg'></div>


5

นี่คือJS_Fiddleขนาดเล็ก

แล้วเป็นวิธีการโทรกลับสแต็คซึ่งสามารถใช้ได้หลังจากสัญญาได้รับการแก้ไขมันเป็นส่วนหนึ่งของห้องสมุดเช่น jQuery แต่ตอนนี้มันมีอยู่ใน JavaScript พื้นเมืองและด้านล่างเป็นคำอธิบายรายละเอียดวิธีการทำงาน

คุณสามารถทำสัญญาในภาษาจาวาสคริปต์ได้เช่นเดียวกับที่มีสัญญาใน jQuery ทุกสัญญาสามารถซ้อนกันและสามารถเรียกได้ด้วย Resolve และ Reject callbacks นี่คือวิธีที่คุณสามารถเชื่อมโยงการโทรแบบอะซิงโครนัส

ฉันแยกและแก้ไขจากเอกสาร MSDN เกี่ยวกับสถานะการชาร์จแบตเตอรี่ ..

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

navigator
    .getBattery()
    .then(function(battery) {
       var charging = battery.charging;
       alert(charging);
    })
    .then(function(){alert("YeoMan : SINGH is King !!");});

ตัวอย่าง es6 อื่น

function fetchAsync (url, timeout, onData, onError) {
    
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject)
    })
}
Promise.all([
    fetchPromised("http://backend/foo.txt", 500),
    fetchPromised("http://backend/bar.txt", 500),
    fetchPromised("http://backend/baz.txt", 500)
]).then((data) => {
    let [ foo, bar, baz ] = data
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`)
}, (err) => {
    console.log(`error: ${err}`)
})

ความหมาย :: แล้วเป็นวิธีการที่ใช้ในการแก้ปัญหาการเรียกกลับ Asynchronous

สิ่งนี้ถูกนำมาใช้ในES6

โปรดหาเอกสารที่เหมาะสมที่นี่สัญญา Es6


คำตอบของคุณไม่ได้ตอบคำถาม มันมีเพียงตัวอย่างของการใช้ API โดยไม่อธิบายว่าthenมาจากที่ใดและทำงานอย่างไร คุณควรปรับปรุงคำตอบเพื่อให้รายละเอียดเหล่านั้น
Didier L

@TarandeepSingh - ในครั้งแรกคำสั่งที่คุณแจ้งเตือนสถานะแบตเตอรี่ไม่มีวัตถุสัญญาจะถูกส่งกลับ แล้วการใช้วินาทีคืออะไร
Mohit Jain

@MohitJain มันแสดงให้เห็นว่าคุณสามารถโทรกลับหลายครั้งได้แม้ว่าคุณจะไม่ได้สัญญาใหม่ เนื่องจากการโทรหลายสายสามารถทำได้ด้วย Promise.all
Tarandeep Singh

WTH คุณหมายถึง " method callback stack " หรือไม่
Bergi

4

ฉันสงสัยว่า doSome คืนค่านี้ซึ่งก็คือ myObj ซึ่งก็มีวิธีการเช่นกัน ผูกมัดวิธีมาตรฐาน ...

ถ้า doSome ไม่ได้คืนสิ่งนี้เป็นวัตถุที่ doSome ถูกดำเนินการโปรดมั่นใจว่ากำลังคืนวัตถุบางอย่างด้วยวิธีการนั้น ...

ในขณะที่ @patrick ชี้ให้เห็นแล้วไม่มี () สำหรับ js มาตรฐาน


1
ฉันสงสัยว่า doSome จะส่งคืนสิ่งนี้ - ไม่มีอะไรบังคับ / แก้ตัวการสงสัยดังกล่าว
Salathiel Genèse

1

doSome ("task") จะต้องส่งคืนวัตถุสัญญาและสัญญานั้นจะมีฟังก์ชันอยู่เสมอดังนั้นรหัสของคุณจึงเป็นเช่นนี้

promise.then(function(env) {
    // logic
}); 

และคุณรู้ว่านี่เป็นเพียงการโทรธรรมดาสู่ฟังก์ชั่นสมาชิก


1

.then ส่งคืนสัญญาในฟังก์ชัน async

ตัวอย่างที่ดีจะเป็น:

var doSome = new Promise(function(resolve, reject){
    resolve('I am doing something');
});

doSome.then(function(value){
    console.log(value);
});

หากต้องการเพิ่มตรรกะอื่นเข้าไปคุณสามารถเพิ่มการreject('I am the rejected param')เรียกใช้ฟังก์ชันและ console.log ได้


0

ในกรณีthen()นี้เป็นวิธีการเรียนของวัตถุส่งกลับโดยdoSome()วิธีการ


0

ฟังก์ชัน ".then ()" เป็น wideley ใช้สำหรับวัตถุที่มีสัญญาในการเขียนโปรแกรม Asynchoronus สำหรับแอพ Windows 8 Store เท่าที่ฉันเข้าใจมันใช้งานได้เหมือนโทรกลับ

ค้นหารายละเอียดในเอกสารนี้ http://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx

สาเหตุอาจเป็นชื่อสำหรับฟังก์ชั่นที่กำหนดอื่น ๆ


-1

ตัวอย่างอื่น:

new Promise(function(ok) {
   ok( 
      /* myFunc1(param1, param2, ..) */
   )
}).then(function(){
     /* myFunc1 succeed */
     /* Launch something else */
     /* console.log(whateverparam1) */
     /* myFunc2(whateverparam1, otherparam, ..) */
}).then(function(){
     /* myFunc2 succeed */
     /* Launch something else */
     /* myFunc3(whatever38, ..) */
})

ตรรกะเดียวกันโดยใช้ฟังก์ชั่นลูกศรจดชวเลข:

new Promise((ok) =>
   ok( 
      /* myFunc1(param1, param2, ..) */
)).then(() =>
     /* myFunc1 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc2(whateverparam1, otherparam, ..) */
).then(() =>
     /* myFunc2 succeed */
     /* Launch something else */
     /* Only ONE call or statment can be made inside arrow functions */
     /* For example, using console.log here will break everything */
     /* myFunc3(whatever38, ..) */
)


-4

ฉันอายุประมาณ 8 ปีแล้วอืม ... ยังไงก็ตามฉันไม่รู้จริง ๆ ว่า () จะทำอะไร แต่ MDN อาจมีคำตอบ จริงๆแล้วฉันอาจจะเข้าใจมากกว่านี้เล็กน้อย

สิ่งนี้จะแสดงข้อมูลทั้งหมด (หวังว่า) คุณต้องการ ถ้าไม่มีใครโพสต์ลิงค์นี้ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

รูปแบบคือ contract.prototype.then () สัญญาและต้นแบบเป็นเหมือนตัวแปร แต่ไม่เหมือนตัวแปรใน javascript ฉันหมายถึงเหมือนสิ่งอื่น ๆ ไปที่นั่นเช่น navigator.getBattery (). แล้ว () ที่นี่มีอยู่จริง แต่เป็น ใช้งานบนเว็บได้แทบจะไม่แสดงสถานะเกี่ยวกับแบตเตอรี่ของอุปกรณ์ข้อมูลเพิ่มเติมและ MDN หากคุณอยากรู้

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