แก้ไข Javascript สัญญานอกขอบเขตฟังก์ชั่น


280

ฉันใช้สัญญา ES6

ตามปกติสัญญาจะสร้างและใช้งานเช่นนี้

new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

แต่ฉันได้ทำบางอย่างเช่นด้านล่างเพื่อแก้ไขปัญหาภายนอกเพื่อความยืดหยุ่น

var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) { 
    outsideResolve = resolve; 
    outsideReject = reject; 
});

และหลังจากนั้น

onClick = function(){
    outsideResolve();
}

ใช้งานได้ดี แต่มีวิธีที่ง่ายกว่านี้หรือไม่ ถ้าไม่เป็นแบบนี้ดีหรือไม่?


2
ฉันไม่คิดว่าจะมีวิธีอื่น ฉันเชื่อว่ามีการระบุว่าการเรียกกลับที่ส่งผ่านไปPromiseจะต้องดำเนินการพร้อมกันเพื่อให้ "ส่งออก" ทั้งสองฟังก์ชั่น
เฟลิกซ์กลิง

1
มันเหมาะกับฉันเหมือนที่คุณเขียน ดังนั้นเท่าที่ฉันกังวลนี้เป็นวิธี "บัญญัติ"
Gilad Barner

14
ฉันคิดว่าควรจะมีวิธีที่เป็นทางการในการบรรลุเป้าหมายนี้ในอนาคต คุณลักษณะนี้มีประสิทธิภาพมากในความคิดของฉันเพราะคุณสามารถรอค่าจากบริบทอื่น ๆ
Jose

เมื่อใดก็ตามที่พวกเขาหาวิธีแก้ไขปัญหานี้อย่างเหมาะสมฉันหวังว่าพวกเขาจะทำให้มันเป็นไปตามสัญญาที่ซ้อนกันซึ่งบางอย่างอาจเกิดขึ้นอีก
Arthur Tarasov

ฉันคิดว่า API สัญญา "แนะนำ" ให้ใช้พวกเขาเป็นค่าตอบแทนเสมอและไม่เคยเป็นวัตถุที่คุณสามารถเข้าถึงหรือโทร ในคำอื่น ๆ บังคับให้เราปฏิบัติต่อพวกเขาเป็นค่าตอบแทนแทนวัตถุที่เราสามารถเข้าถึงหรือฟังก์ชั่นที่เราสามารถเรียกหรือสิ่งที่เราสามารถอ้างอิงกับตัวแปรหรือผ่านเป็นพารามิเตอร์ ฯลฯ หากคุณเริ่มใช้สัญญาเป็นวัตถุอื่น ๆ ที่คุณอาจจะ ท้ายที่สุดจำเป็นต้องแก้ไขจากภายนอกเช่นเดียวกับในคำถามของคุณ ... ที่ถูกกล่าวว่าฉันยังคิดว่าควรมีวิธีที่เป็นทางการในการทำเช่นนี้ ... และ Deferred ดูเหมือนจะเป็นวิธีแก้ปัญหาสำหรับฉัน
cancerbero

คำตอบ:


93

ไม่ไม่มีวิธีอื่นในการทำสิ่งนี้สิ่งเดียวที่ฉันสามารถพูดได้ก็คือกรณีการใช้งานนี้ไม่ธรรมดา อย่างที่เฟลิกซ์พูดในความคิดเห็น - สิ่งที่คุณทำจะได้ผลอย่างสม่ำเสมอ

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

ด้วยเหตุผลด้านความปลอดภัยการโยนผู้สร้างสัญญาได้รับเลือกจากการรอการตัดบัญชี (ซึ่งเป็นวิธีการสร้างสัญญาที่เป็นทางเลือกที่อนุญาตให้คุณทำในสิ่งที่) - สำหรับแนวทางปฏิบัติที่ดีที่สุด - ฉันจะผ่านองค์ประกอบและใช้ผู้สร้างสัญญาแทน:

var p = new Promise(function(resolve, reject){
    this.onclick = resolve;
}.bind(this));

ด้วยเหตุผลนี้ - เมื่อใดก็ตามที่คุณสามารถใช้คอนสตรัคเตอร์สัญญามากกว่าการส่งออกฟังก์ชั่น - ฉันขอแนะนำให้คุณใช้มัน เมื่อใดก็ตามที่คุณสามารถหลีกเลี่ยงทั้งสอง - หลีกเลี่ยงทั้งสองและโซ่

โปรดทราบว่าคุณไม่ควรใช้เครื่องมือสร้างคำสัญญาสำหรับสิ่งต่าง ๆif(condition)ตัวอย่างแรกสามารถเขียนเป็น:

var p = Promise[(someCondition)?"resolve":"reject"]();

2
สวัสดีเบนจามิน! ขณะนี้ยังไม่มีวิธีที่ดีกว่าในการรับน้ำตาลที่สัญญาอร่อยถ้าเรายังไม่รู้ว่าเมื่อไรที่สัญญาจะสำเร็จหรือไม่ ชอบรูปแบบการรอ / แจ้งแบบอะซิงโครนัสบ้างไหม? เช่นตัวอย่างเช่น "ร้านค้า" และต่อมาก่อให้เกิดPromiseห่วงโซ่? เช่นในกรณีเฉพาะของฉันฉันอยู่บนเซิร์ฟเวอร์กำลังรอการตอบกลับของลูกค้าที่เฉพาะเจาะจง (การจับมือแบบ SYN-ACK-kinda เพื่อให้แน่ใจว่าสถานะการอัปเดตไคลเอ็นต์สำเร็จ)
Domi

1
@Domi ลองใช้การเชื่อมต่อ q และ RxJS
Benjamin Gruenbaum

2
ฉันจะใช้ fetch API เดียวกันได้อย่างไร
Vinod Sobale

95
ไม่ธรรมดา ท้ายที่สุดฉันต้องการมันเกือบทุกโครงการ
Tomáš Zato - Reinstate Monica

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

130

ง่าย:

var promiseResolve, promiseReject;

var promise = new Promise(function(resolve, reject){
  promiseResolve = resolve;
  promiseReject = reject;
});

promiseResolve();

2
@ruX ในฐานะที่เป็นคำตอบที่ได้รับการยอมรับ - มันถูกออกแบบด้วยวิธีนี้ตามวัตถุประสงค์ ประเด็นก็คือว่าถ้ามีข้อยกเว้นจะถูกโยนมันจะถูกจับโดยผู้สร้างสัญญา คำตอบนี้ (เช่นเดียวกับเหมือง) promiseResolve()มีหลุมพรางของการขว้างปาอาจจะมีข้อยกเว้นสำหรับรหัสสิ่งที่โทร ความหมายของคำสัญญาคือมันจะส่งกลับค่าเสมอ นอกจากนี้มันยังใช้งานได้เหมือนโพสต์ของ OP ฉันไม่ได้รับปัญหาอะไรที่แก้ในวิธีที่ใช้ซ้ำได้
Jon Jaques

4
@JonJaques ฉันไม่แน่ใจว่าสิ่งที่คุณพูดจริงหรือไม่ รหัสที่เรียกpromiseResolve()จะไม่ส่งข้อยกเว้น คุณสามารถกำหนด.catchบนตัวสร้างและไม่ว่ารหัสใดเรียกมันตัวสร้าง.catchจะถูกเรียก นี่คือ jsbin ที่แสดงให้เห็นถึงวิธีการทำงานของมัน: jsbin.com/yicerewivo/edit?js,console
carter

ใช่มันถูกจับได้เพราะคุณหุ้มคอนสตรัคสัญญารายอื่นไว้รอบตัว - ตรงประเด็นที่ฉันพยายามทำ อย่างไรก็ตามสมมติว่าคุณมีรหัสอื่น ๆ ที่พยายามเรียกการแก้ไข () ด้านนอกของตัวสร้าง (หรือวัตถุที่ถูกเลื่อนออกไป) ... อาจทำให้เกิดข้อยกเว้นและไม่ถูกจับได้jsbin.com/cokiqiwapo/1/edit?js,console
Jon Jaques

8
ฉันไม่แน่ใจด้วยซ้ำว่ามันเป็นการออกแบบที่ไม่ดี ข้อผิดพลาดที่เกิดขึ้นนอกสัญญาไม่ควรถูกตรวจจับภายในสัญญา มันอาจเป็นตัวอย่างของความเข้าใจผิดหรือความเข้าใจที่ไม่ดีถ้าผู้ออกแบบคาดว่าจะเกิดข้อผิดพลาดขึ้น
KalEl

3
โครงสร้างที่แน่นอนนี้ถูกกล่าวถึงแล้วในคำถาม คุณเคยอ่านมันหรือยัง
Cedric Reichenbach

103

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

การปฏิบัติที่ไร้เดียงสา:

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject)=> {
      this.reject = reject
      this.resolve = resolve
    })
  }
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(()=> {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(result => {
  console.log(result) // 42
})

เวอร์ชั่น ES5:

function Deferred() {
  var self = this;
  this.promise = new Promise(function(resolve, reject) {
    self.reject = reject
    self.resolve = resolve
  })
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(function() {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(function(result) {
  console.log(result) // 42
})

1
อย่าสังเกตการกำหนดขอบเขตคำศัพท์ที่นี่
Florrie

1
ไม่มีความแตกต่างในทางปฏิบัติไม่ว่าจะเป็นresolve|rejectที่ได้รับมอบหมาย lexically bindหรือผ่าน นี่เป็นเพียงการใช้งานอย่างง่ายของวัตถุjQuery Deferredที่มีมาตั้งแต่ 1.0 (ish) มันทำงานเหมือนสัญญายกเว้นจะไม่มีความปลอดภัยจากการโยน ประเด็นทั้งหมดของคำถามนี้คือวิธีบันทึกรหัสสองสามบรรทัดเมื่อสร้างสัญญา
Jon Jaques

1
การใช้การเลื่อนเวลาเป็นวิธีปกติในการทำสิ่งนี้ฉันไม่รู้เลยว่าทำไมถึงไม่สูงกว่านี้
BlueRaja - Danny Pflughoeft

1
คำตอบที่ยอดเยี่ยม! กำลังมองหาฟังก์ชั่นรอการตัดบัญชีที่ jQuery นำเสนอ
Anshul Koka

2
ถูกDeferredเลิก?
Pacerier

19

วิธีการแก้ปัญหาที่ฉันเกิดขึ้นในปี 2015 สำหรับกรอบงานของฉัน ฉันเรียกภารกิจประเภทนี้ว่าสัญญา

function createPromise(handler){
  var _resolve, _reject;

  var promise = new Promise(function(resolve, reject){
    _resolve = resolve; 
    _reject = reject;

    handler(resolve, reject);
  })

  promise.resolve = _resolve;
  promise.reject = _reject;

  return promise;
}

var promise = createPromise()
promise.then(function(data){ alert(data) })

promise.resolve(200) // resolve from outside

4
ขอบคุณสิ่งนี้ใช้ได้ แต่ตัวจัดการคืออะไร? ฉันต้องลบมันเพื่อให้มันใช้งานได้
Sahid

16

ฉันชอบ @JonJaques คำตอบ แต่ฉันต้องการที่จะก้าวไปอีกขั้น

หากคุณผูกthenและcatchแล้วDeferredวัตถุแล้วก็ดำเนินการอย่างเต็มที่PromiseAPI และคุณสามารถรักษามันเป็นสัญญาและความawaitมันและเช่น

class DeferredPromise {
  constructor() {
    this._promise = new Promise((resolve, reject) => {
      // assign the resolve and reject functions to `this`
      // making them usable on the class instance
      this.resolve = resolve;
      this.reject = reject;
    });
    // bind `then` and `catch` to implement the same interface as Promise
    this.then = this._promise.then.bind(this._promise);
    this.catch = this._promise.catch.bind(this._promise);
    this[Symbol.toStringTag] = 'Promise';
  }
}

const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
  deferred.resolve('whoa!');
}, 2000);

async function someAsyncFunction() {
  const value = await deferred;
  console.log(value);
}

someAsyncFunction();


10

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

function Deferred() {
    let resolve;
    let reject;
    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

การใช้งานจะเป็น

const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
    confirm: resolve,
    cancel: reject
});
return promise;

ซึ่งคล้ายกับ jQuery

const dfd = $.Deferred();
displayConfirmationDialog({
    confirm: dfd.resolve,
    cancel: dfd.reject
});
return dfd.promise();

แม้ว่าในกรณีที่ใช้ง่ายนี้ไวยากรณ์ดั้งเดิมนั้นใช้ได้

return new Promise((resolve, reject) => {
    displayConfirmationDialog({
        confirm: resolve,
        cancel: reject
    });
});

8

ฉันใช้ฟังก์ชันผู้ช่วยเพื่อสร้างสิ่งที่ฉันเรียกว่า "สัญญาแบบแบน" -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
      resolve = res;
      reject = rej;
    });

    return { promise, resolve, reject };
}

และฉันใช้มันอย่างนั้น -

function doSomethingAsync() {

    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;

}

ดูตัวอย่างการทำงานเต็ม -

แก้ไข: ฉันได้สร้างแพคเกจ NPM ชื่อflat-contractและมีรหัสใน GitHubด้วย


7

คุณสามารถสรุปคำสัญญาในชั้นเรียนได้

class Deferred {
    constructor(handler) {
        this.promise = new Promise((resolve, reject) => {
            this.reject = reject;
            this.resolve = resolve;
            handler(resolve, reject);
        });

        this.promise.resolve = this.resolve;
        this.promise.reject = this.reject;

        return this.promise;
    }
    promise;
    resolve;
    reject;
}

// How to use.
const promise = new Deferred((resolve, reject) => {
  // Use like normal Promise.
});

promise.resolve(); // Resolve from any context.

6

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

function defer(obj) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
}

นี่คือตัวอย่างที่ง่ายของการใช้เวอร์ชันนี้defer()เพื่อรวมการFontFaceโหลด Promise กับกระบวนการ async อื่น:

function onDOMContentLoaded(evt) {
    let all = []; // array of Promises
    glob = {};    // global object used elsewhere
    defer(glob);
    all.push(glob.promise);
    // launch async process with callback = resolveGlob()

    const myFont = new FontFace("myFont", "url(myFont.woff2)");
    document.fonts.add(myFont);
    myFont.load();
    all.push[myFont];
    Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
    glob.resolve();
}
function runIt() {} // runs after all promises resolved 

อัปเดต: 2 ทางเลือกในกรณีที่คุณต้องการห่อหุ้มวัตถุ:

function defer(obj = {}) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
    return obj;
}
let deferred = defer();

และ

class Deferred {
    constructor() {
        this.promise = new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject  = reject;
        });
    }
}
let deferred = new Deferred();

หากคุณใช้ตัวอย่างเหล่านี้ในฟังก์ชั่น async คุณจะต้องอ้างถึงคุณสมบัติของสัญญาเมื่อคุณต้องการใช้มูลค่าของสัญญาที่ได้รับการแก้ไข:const result = await deferred.promise;
b00t

6

คำตอบที่ยอมรับนั้นผิด มันง่ายมากที่จะใช้ขอบเขตและการอ้างอิงถึงแม้ว่ามันอาจจะทำให้นักปราชญ์สัญญาโกรธ:

const createPromise = () => {
    let resolver;
    return [
        new Promise((resolve, reject) => {
            resolver = resolve;
        }),
        resolver,
    ];
};

const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);

เรากำลังคว้าการอ้างอิงไปยังฟังก์ชั่นการแก้ปัญหาเมื่อสัญญาถูกสร้างขึ้นและเรากลับมาเพื่อให้สามารถตั้งค่าภายนอก

ในหนึ่งวินาทีคอนโซลจะแสดงผล:

> foo

ฉันคิดว่านี่เป็นวิธีที่ดีที่สุด สิ่งเดียวคือรหัสอาจเป็น verbose น้อยกว่าเล็กน้อย
pie6k

ดี! ความคิดที่ฉลาด +50 ถ้าฉันทำได้
Mitya

นี่เป็นเพียงสิ่งที่ OP ทำ ในความเป็นจริงคุณกำลังคิดค้นรูปแบบรอการตัดบัญชีใหม่มากกว่าสัญญาแน่นอนว่าเป็นไปได้และแนวทางของคุณใช้งานได้ (เป็นรหัส OP เริ่มต้น) แต่นี่ไม่ใช่วิธีปฏิบัติที่ดีที่สุดเนื่องจาก "โยนเหตุผลด้านความปลอดภัย" ที่อธิบายไว้ในคำตอบที่ยอมรับ
dhilt

4

ใช่คุณสามารถ. โดยใช้CustomEventAPI สำหรับสภาพแวดล้อมของเบราว์เซอร์ และการใช้โครงการตัวปล่อยเหตุการณ์ในสภาพแวดล้อม node.js เนื่องจากตัวอย่างในคำถามนั้นมีไว้สำหรับสภาพแวดล้อมของเบราว์เซอร์นี่คือตัวอย่างการทำงานของสิ่งเดียวกัน

function myPromiseReturningFunction(){
  return new Promise(resolve => {
    window.addEventListener("myCustomEvent", (event) => {
       resolve(event.detail);
    }) 
  })
}


myPromiseReturningFunction().then(result => {
   alert(result)
})

document.getElementById("p").addEventListener("click", () => {
   window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>

ฉันหวังว่าคำตอบนี้มีประโยชน์!


3

วิธีการแก้ปัญหาของเราคือการใช้การปิดเพื่อจัดเก็บฟังก์ชั่นแก้ไข / ปฏิเสธและแนบฟังก์ชั่นเพิ่มเติมเพื่อขยายสัญญา

นี่คือรูปแบบ:

function getPromise() {

    var _resolve, _reject;

    var promise = new Promise((resolve, reject) => {
        _reject = reject;
        _resolve = resolve;
    });

    promise.resolve_ex = (value) => {
       _resolve(value);
    };

    promise.reject_ex = (value) => {
       _reject(value);
    };

    return promise;
}

และใช้มัน:

var promise = getPromise();

promise.then(value => {
    console.info('The promise has been fulfilled: ' + value);
});

promise.resolve_ex('hello');  
// or the reject version 
//promise.reject_ex('goodbye');

2
ดีมาก ... ฉันแค่เรียนรู้คำมั่นสัญญา แต่ก็งงงวยอย่างต่อเนื่องโดยความจริงที่ว่าคุณไม่สามารถแก้ไขได้ว่า "ที่อื่น" การใช้การปิดเพื่อซ่อนรายละเอียดการใช้งานเป็นความคิดที่ดี ... แต่อันที่จริงฉันไม่แน่ใจว่าเป็นสิ่งที่คุณทำ: แทนที่จะมีตัวแปรส่วนตัว "หลอก" ฉันค่อนข้างแน่ใจว่ามีวิธีปกปิดตัวแปรอย่างสมบูรณ์ซึ่งควรจะไม่สามารถเข้าถึงได้ ... ซึ่งจริงๆสิ่งที่ฝาปิดหมายถึง ...
ไมค์หนู

> การปิดเป็นกลุ่มของรหัสที่สามารถอ้างอิง (และผ่านไป) ด้วยการเข้าถึงตัวแปรของขอบเขตการปิดล้อม var _resolve, _reject; เป็นขอบเขตล้อมรอบ
Steven Spungin

ใช่ยุติธรรมพอ ที่จริงแล้วฉันคิดว่าคำตอบของฉันคือสิ่งที่ซับซ้อนเกินไปและยิ่งไปกว่านั้นคำตอบของคุณสามารถทำให้ง่ายขึ้น: คุณเพียงแค่ต้องไปpromise.resolve_ex = _resolve; promise.reject_ex = _reject;... ยังทำงานได้ดี
ไมค์หนู

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

2
คำถามคือวิธีการแก้ไขนอกขอบเขต นี่คือทางออกที่ได้ผลและในการผลิตของเราเรามีเหตุผลที่จำเป็นจริง ๆ ที่จะทำ ฉันไม่เห็นว่าทำไมการแก้ปัญหาดังกล่าวจึงสมควรได้รับ downvote
Steven Spungin

2

ฉันพบว่าตัวเองขาดรูปแบบที่เลื่อนออกไปเช่นกันในบางกรณี คุณสามารถสร้างหนึ่งสัญญาบน ES6:

export default class Deferred<T> {
    private _resolve: (value: T) => void = () => {};
    private _reject: (value: T) => void = () => {};

    private _promise: Promise<T> = new Promise<T>((resolve, reject) => {
        this._reject = reject;
        this._resolve = resolve;
    })

    public get promise(): Promise<T> {
        return this._promise;
    }

    public resolve(value: T) {
        this._resolve(value);
    }

    public reject(value: T) {
        this._reject(value);
    }
}

2

ขอบคุณทุกคนที่โพสต์ในกระทู้นี้ ฉันสร้างโมดูลที่มีวัตถุ Defer () ที่อธิบายไว้ก่อนหน้านี้รวมถึงวัตถุอื่น ๆ สองสามตัวที่สร้างขึ้นบนนั้น พวกเขาใช้ประโยชน์จากสัญญาและไวยากรณ์สัญญาโทรกลับเรียบร้อยเพื่อใช้การสื่อสาร / การจัดการเหตุการณ์ภายในโปรแกรม

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

    rp = require("repeatable-promise")

    https://github.com/CABrouwers/repeatable-promise


1

ฉันเขียน lib เล็กน้อยสำหรับสิ่งนี้ https://www.npmjs.com/package/@inf3rno/promise.exposed

ผมใช้วิธีการโรงงานอื่น ๆ เขียน แต่ฉันลบล้างthen, catch, finallyวิธีเกินไปดังนั้นคุณจึงสามารถแก้ไขสัญญาเดิมโดยเหล่านั้นเช่นกัน

การแก้ไขสัญญาโดยไม่มีผู้ดำเนินการจากภายนอก:

const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");

แข่งกับ setTimeout ของผู้บริหารจากภายนอก:

const promise = Promise.exposed(function (resolve, reject){
    setTimeout(function (){
        resolve("I almost fell asleep.")
    }, 100000);
}).then(console.log);

setTimeout(function (){
    promise.resolve("I don't want to wait that much.");
}, 100);

มีโหมดไม่มีข้อขัดแย้งหากคุณไม่ต้องการสร้างมลภาวะเนมสเปซส่วนกลาง:

const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");

1

ฉันสร้างห้องสมุดที่เรียกmanual-promiseว่าทำหน้าที่แทนการPromiseดรอป ไม่มีคำตอบอื่น ๆ ที่นี่จะทำงานเป็นแบบแทนที่ในPromiseขณะที่พวกเขาใช้พร็อกซี่หรือห่อ

yarn add manual-promise

npn install manual-promise


import { ManualPromise } from "manual-promise";

const prom = new ManualPromise();

prom.resolve(2);

// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
    // ... code
});


new ManualPromise() instanceof Promise === true

https://github.com/zpxp/manual-promise#readme


0

วิธีการเกี่ยวกับการสร้างฟังก์ชั่นเพื่อหักล้างการปฏิเสธและคืน

function createRejectablePromise(handler) {
  let _reject;

  const promise = new Promise((resolve, reject) => {
    _reject = reject;

    handler(resolve, reject);
  })

  promise.reject = _reject;
  return promise;
}

// Usage
const { reject } = createRejectablePromise((resolve) => {
  setTimeout(() => {
    console.log('resolved')
    resolve();
  }, 2000)

});

reject();

0

ฉันได้รวบรวมส่วนสำคัญที่ทำงาน: https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13

นี่คือวิธีที่คุณควรใช้:

import ExternalizedPromiseCreator from '../externalized-promise';

describe('ExternalizedPromise', () => {
  let fn: jest.Mock;
  let deferredFn: jest.Mock;
  let neverCalledFn: jest.Mock;
  beforeEach(() => {
    fn = jest.fn();
    deferredFn = jest.fn();
    neverCalledFn = jest.fn();
  });

  it('resolve should resolve the promise', done => {
    const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());

    externalizedPromise
      .promise
      .then(() => deferredFn())
      .catch(() => neverCalledFn())
      .then(() => {
        expect(deferredFn).toHaveBeenCalled();
        expect(neverCalledFn).not.toHaveBeenCalled();
        done();
      });

    expect(fn).toHaveBeenCalled();
    expect(neverCalledFn).not.toHaveBeenCalled();
    expect(deferredFn).not.toHaveBeenCalled();

    externalizedPromise.resolve();
  });
  ...
});

0

เปิดใช้งานก่อน --allow-natives-syntax บนเบราว์เซอร์หรือโหนด

const p = new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

onClick = function () {
    %ResolvePromise(p, value)
}

0

เป็นอีกทางเลือกหนึ่งในการแก้ไขสัญญาจากภายนอก

 class Lock {
        #lock;  // Promise to be resolved (on  release)
        release;  // Release lock
        id;  // Id of lock
        constructor(id) {
            this.id = id
            this.#lock = new Promise((resolve) => {
                this.release = () => {
                    if (resolve) {
                        resolve()
                    } else {
                        Promise.resolve()
                    }
                }
            })
        }
        get() { return this.#lock }
    }

การใช้

let lock = new Lock(... some id ...);
...
lock.get().then(()=>{console.log('resolved/released')})
lock.release()  // Excpected 'resolved/released'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.