คำสัญญาสามารถมีข้อโต้แย้งหลายข้อถึง onFulfilled ได้หรือไม่?


127

ฉันทำตามข้อมูลจำเพาะที่นี่และฉันไม่แน่ใจว่าอนุญาตให้เรียก onFulfilled ด้วยอาร์กิวเมนต์หลายรายการหรือไม่ ตัวอย่างเช่น:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled('arg1', 'arg2');
})

ดังนั้นรหัสของฉัน:

promise.then(function(arg1, arg2){
    // ....
});

จะได้รับทั้งสองarg1และarg2?

ฉันไม่สนใจว่าจะมีการดำเนินการตามสัญญาใด ๆ อย่างไรฉันต้องการทำตามข้อกำหนด w3c สำหรับคำสัญญาอย่างใกล้ชิด


ตามคำใบ้ฉันพบว่าการใช้github.com/then/promise (ซึ่งเป็นการใช้งานแบบแบร์โบน) แสดงให้เห็นว่ามันไม่ได้ให้อาร์กิวเมนต์ที่ 2
badunk

2
คุณต้องการใช้ Bluebird กับ. spread - นอกจากนี้ยังหยุดการดูแลเกี่ยวกับสเปค, สเปคเป็นข้อมูลเกี่ยวกับการทำงานร่วมกันระหว่างการใช้งานและมีน้อยโดยการออกแบบ
Benjamin Gruenbaum

คำตอบ:


130

ฉันทำตามข้อมูลจำเพาะที่นี่และฉันไม่แน่ใจว่าอนุญาตให้เรียก onFulfilled ด้วยอาร์กิวเมนต์หลายรายการหรือไม่

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

ฉันไม่สนใจว่าจะมีการดำเนินการตามสัญญาใด ๆ อย่างไรฉันต้องการทำตามข้อกำหนด w3c สำหรับคำสัญญาอย่างใกล้ชิด

นั่นคือสิ่งที่ฉันเชื่อว่าคุณคิดผิด ข้อกำหนดถูกออกแบบมาให้น้อยที่สุดและสร้างขึ้นเพื่อการทำงานร่วมกันระหว่างไลบรารีสัญญา แนวคิดคือการมีชุดย่อยที่ตัวอย่างเช่นฟิวเจอร์ส DOM สามารถใช้งานได้อย่างน่าเชื่อถือและไลบรารีสามารถใช้งานได้ การใช้งานตามสัญญาจะทำในสิ่งที่คุณขอ.spreadมาระยะหนึ่งแล้ว ตัวอย่างเช่น:

Promise.try(function(){
    return ["Hello","World","!"];
}).spread(function(a,b,c){
    console.log(a,b+c); // "Hello World!";
});

ด้วยคราม วิธีแก้ปัญหาอย่างหนึ่งหากคุณต้องการฟังก์ชันนี้คือการเติมเต็ม

if (!Promise.prototype.spread) {
    Promise.prototype.spread = function (fn) {
        return this.then(function (args) {
            return Promise.all(args); // wait for all
        }).then(function(args){
         //this is always undefined in A+ complaint, but just in case
            return fn.apply(this, args); 
        });
    };
}

สิ่งนี้ช่วยให้คุณทำ:

Promise.resolve(null).then(function(){
    return ["Hello","World","!"]; 
}).spread(function(a,b,c){
    console.log(a,b+c);    
});

ด้วยสัญญาพื้นเมืองที่ง่ายซอ หรือใช้การแพร่กระจายซึ่งปัจจุบัน (2018) เป็นเรื่องธรรมดาในเบราว์เซอร์:

Promise.resolve(["Hello","World","!"]).then(([a,b,c]) => {
  console.log(a,b+c);    
});

หรือรอ:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);

2
โปรดทราบว่าไลบรารีอื่น ๆ (เช่น Q) ก็รองรับ.spreadเช่น Bluebird เช่นกันเหตุผลที่ไม่อยู่ในข้อกำหนดคือการรักษา spec ให้น้อยที่สุดถือเป็นเรื่องใหญ่จริงๆเพื่อให้สามารถทำงานร่วมกันระหว่างโค้ดและไลบรารีได้
Benjamin Gruenbaum

ข้อสังเกตที่สอง - คุณอาจต้องการเรียกPromise.allใช้อาร์เรย์ก่อนที่จะใช้ฟังก์ชันแทนที่จะ.thenใช้เพื่อจัดการกับไลบรารีน้ำตาลที่มีให้ ไม่บังคับ แต่ก็น่ารักดี
Benjamin Gruenbaum

1
Promies.all มีผลบังคับใช้กับการใช้งานของคุณแม้ว่าคุณจะสามารถเปลี่ยนการใช้งานเป็นreturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija

14
spreadคือการหยุดชะงัก ES6 แนะนำการทำลายโครงสร้างและตัวดำเนินการส่วนที่เหลือ / การแพร่กระจายซึ่งไม่จำเป็นต้องใช้spreadทันที .then(([a, b, c]) => {})
Kris Kowal

3
@KrisKowal โปรดทราบว่า. spread () โดยปริยายทำ. all () แต่ไวยากรณ์การทำลาย ES6 ไม่ได้ -> bluebirdjs.com/docs/api/spread.html
Gomino

66

คุณสามารถใช้การทำลายล้าง E6:

การทำลายวัตถุ:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled({arg1: value1, arg2: value2});
})

promise.then(({arg1, arg2}) => {
    // ....
});

การทำลายโครงสร้างอาร์เรย์:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled([value1, value2]);
})

promise.then(([arg1, arg2]) => {
    // ....
});

3
ตัวอย่างที่ดีและเป็นประโยชน์สำหรับคำตอบนี้!
Rahul Verma

19

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


4

เท่าที่ฉันสามารถบอกได้ว่าการอ่านข้อกำหนดES6 Promiseและข้อกำหนดสัญญามาตรฐานไม่มีข้อใดที่ขัดขวางการใช้งานจากการจัดการกรณีนี้ - อย่างไรก็ตามไม่มีการนำไปใช้ในไลบรารีต่อไปนี้:

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

new Promise(function(resolve, reject) {
   return resolve(5, 4);
})
.then(function(x,y) {
   console.log(y);
   return x; //we can only return 1 value here so the next then will only have 1 argument
})
.then(function(x,y) {
    console.log(y);
});

8
Q ไม่สนับสนุนความละเอียดหลายค่าเนื่องจากสัญญาทำหน้าที่เป็นพร็อกซีสำหรับผลลัพธ์ของการเรียกใช้ฟังก์ชัน แต่ยังสามารถใช้พร็อกซีสำหรับวัตถุระยะไกลได้ ในทั้งสองกรณีนี้อาร์เรย์เป็นเพียงตัวแทนของค่าผสมที่สมเหตุสมผลเท่านั้น ด้วยการเพิ่มการทำลายโครงสร้างและการ "แพร่กระจาย" อาร์กิวเมนต์ใน ES6 ไวยากรณ์จะดีมาก วิธีการ "แพร่กระจาย" คือการหยุดชะงัก
Kris Kowal

คุณสามารถreturn Promise.of(x, y)ใช้แทนค่าสเกลาร์จากการthenเรียกกลับได้เสมอ
Bergi

2

นี่คือโซลูชัน CoffeeScript

ฉันกำลังมองหาวิธีแก้ปัญหาเดียวกันและพบว่ามีบางสิ่งที่รบกวนมากจากคำตอบนี้: การปฏิเสธสัญญาที่มีข้อโต้แย้งหลายข้อ (เช่น $ http) ใน AngularJS

คำตอบของผู้ชายคนนี้Florian

promise = deferred.promise

promise.success = (fn) ->
  promise.then (data) ->
   fn(data.payload, data.status, {additional: 42})
  return promise

promise.error = (fn) ->
  promise.then null, (err) ->
    fn(err)
  return promise

return promise 

และวิธีใช้:

service.get().success (arg1, arg2, arg3) ->
    # => arg1 is data.payload, arg2 is data.status, arg3 is the additional object
service.get().error (err) ->
    # => err

ควร->จะเป็น=>?
SherylHohman

1
@SherylHohman ย้อนกลับไปในปี 2015 สิ่งนี้เขียนด้วย CoffeeScript ( coffeescript.org/#introduction ) ไม่ใช่ไวยากรณ์ ES6 ลูกศรธรรมดาเป็นฟังก์ชันที่เรียบง่ายและลูกศรไขมันเกือบจะเหมือนกับ ES6 (ฉันเดาว่าลูกศรไขมัน ES6 ถูกยืมมาจาก CoffeScript มากหรือน้อย)
Val Entin

@SherylHohman แก้ไขโพสต์ใน ECMA ได้ตามต้องการ
Val Entin

ขอบคุณสำหรับคำตอบของคุณ ฉันจะแก้ไขเพื่อชี้แจงว่านี่เป็นโซลูชันสคริปต์กาแฟเท่านั้น ด้วยเหตุนี้คำตอบของคุณจึงเป็นและอาจเป็นประโยชน์สำหรับฐานรหัส CoffeeScript ขอบคุณสำหรับข้อเสนอของคุณในการแก้ไข: 1) ฉันไม่คุ้นเคยกับ CoffeeScript มากพอที่จะเสี่ยงต่อการแก้ไข / ทำลายโซลูชันของคุณ ;-) 2) การแปลโค้ดของคุณเป็น JS ที่ทันสมัยควรถือเป็นการเบี่ยงเบนจาก "เจตนาเดิมของคำตอบของคุณ" ดังนั้นจึงไม่ควรผ่านการตรวจสอบ 'แก้ไข' แต่อาจมีคนโพสต์คำตอบใหม่ได้หากมีความโน้มเอียงมากเกินไปให้แปลรหัสของคุณ ตาม
หลักการ

0

คำถามที่ดีและคำตอบที่ดีโดย Benjamin, Kris และอื่น ๆ - ขอบคุณมาก!

ฉันใช้ในโครงการและได้สร้างโมดูลซึ่งเป็นไปตามรหัสเบนจามิน Gruenwald ของ พร้อมใช้งานบน npmjs:

npm i -S promise-spread

จากนั้นในโค้ดของคุณให้ทำ

require('promise-spread');

หากคุณกำลังใช้ไลบรารีเช่น any-promise

var Promise = require('any-promise');
require('promise-spread')(Promise);

บางทีคนอื่นก็พบว่ามีประโยชน์เช่นกัน!


0

การยกเลิกการกำหนดโครงสร้างใน ES6 จะช่วยได้ที่นี่สำหรับตัวอย่าง:

let [arg1, arg2] = new Promise((resolve, reject) => {
    resolve([argument1, argument2]);
});

0

เนื่องจากฟังก์ชันใน Javascript สามารถเรียกใช้ด้วยอาร์กิวเมนต์จำนวนเท่าใดก็ได้และเอกสารไม่ได้กำหนดข้อ จำกัด ใด ๆ เกี่ยวกับonFulfilled()อาร์กิวเมนต์ของเมธอดนอกเหนือจากส่วนคำสั่งด้านล่างฉันคิดว่าคุณสามารถส่งอาร์กิวเมนต์หลายตัวไปยังonFulfilled()เมธอดได้ตราบเท่าที่ค่าของสัญญาคือ อาร์กิวเมนต์แรก

2.2.2.1 จะต้องถูกเรียกหลังจากที่สัญญาสำเร็จโดยมีค่าของสัญญาเป็นอาร์กิวเมนต์แรก


-1

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

ฉันมักจะมองไปที่หน้านี้สำหรับคำถามพื้นฐานเกี่ยวกับสัญญาโปรดแจ้งให้เราทราบหากฉันผิด

http://www.html5rocks.com/en/tutorials/es6/promises/


1
ไม่ถูกต้องnew Promiseมีไวยากรณ์function(resolve, error)ในขณะที่thenมีไวยากรณ์.then(function(arg) {
megawac

2
@megawac มันถูกต้องจริงๆแล้วใส่ไม่ดี - จากนั้นยอมรับข้อโต้แย้งสอง (บางครั้ง 3) - มันค่อนข้างผิดปกติ
Benjamin Gruenbaum

@BenjaminGruenbaum afaik มัน .then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac

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