简体   繁体   中英

Javascript - Defer Promise resolution

I have a service that produce some confirm-alert and wait for user interaction, managed by a Promise.

If a new confirm-alert is produced before the user has answered the first one, the service will store it in a sort of "waiting-list" and will show it as soon as the previous one will be dismissed by the user

let waitList = []
let activeAlert = null;

function createAlert(){
   if(!activeAlert){
       let pr = new Promise((resolve, reject) => {
           // do stuff to show the alert
           if('user click ok') resolve();
           else reject();
       });
       activeAlert = pr;
       return pr;
   }
   else {
       let p = new Promise();
       waitList.push(p);
       return p;
   }
}

let alert = createAlert();
alert.then(()=>{
    // the user clicked OK
}).catch(()=>{
    // the user click CANCEL
});

let alert2 = createAlert();
alert2.then(()=>{
    // the user clicked OK on the second one
}).catch(()=>{
    // the user click CANCEL on the second one
});

I am aware of the concept of Promise anti-pattern and that the Defer object is deprecated and considered obsolete. I can not understand how I can define the "resolve" and "reject" conditions for the Promise that is saved in the array.

Promises cannot be resolved from outside their scope without saving some references first. If you want to solve them from outside the array, you should also push the related resolve and reject callbacks into an object so that they can be used from outside that scope. EX:

let myRefHolder = {};
let toResolve = new Promise((res, rej) => {
    myRefHolder.resolutionCallback = res;
    //other code
});

And then in the even handler:

clickHandler = () => {
    myRefHolder.resolutionCallback(resolutionValue);
    //other code
}

Then you just have to change this structure a bit to accomodate all your promises and callbacks... Instead of an myRefHolder object, you could have an array of objects shaped like myRefHolders , once for each Promise!

An alternative is assigning the button event handler directly in the Promise:

new Promise((rej, res) => yourButtonElement.addEventListener(‘click’, res));

This will be solved when you click on the button. Instead of ref you could also pass a more complicated inline function that has more complicated logic. Since an event can have multiple handlers, you can keep adding to that event for each of your promises - but it could get messy because you then have to remove the listener before actually resolving the Promise (the example 1 line code I used it's likely going to leak memory because that handler stays alive, and keeps Garbage Collector from freeing up the Promise memory)

If what you're asking is how do I then/catch a specific promise in an array of promises, it's as easy as myArray[index].then(() => {}) If you're waiting on an array of promises, as easy as Promise.all(myArray)

You can keep a promise chain for previous alerts (separate from the alert's own promise, so you can resolve/reject those as appropriate), and then have each promise resolve when the alert is completed:

// Kick things off with a resolved promise
let lastPromise = Promise.resolve();

function createAlert(msg) {
  // Create the promise for this alert, but don't start it yet
  let resolve, reject;
  const p = new Promise((_resolve, _reject) => {
    resolve = _resolve;
    reject = _reject;
  });
  // Wait until the last alert is complete before starting this one
  lastPromise = lastPromise.then(() => {
    // Start this alert, and when it's done, resolve our chain promise
    // ...
    // later, if user confirms or whatever:
      resolve();
    // or if they cancel or whatever:
      reject();
    // Slave our chain to the resolution/rejection of the alert
    return p.catch(e => undefined).then(v => undefined);
  });
  // Return the alert promise
  return p;
}

Running example with a very crude "dialog" with OK and Cancel buttons, which resolve and reject the promise respectively:

 // Kick things off with a resolved promise let lastPromise = Promise.resolve(); function createAlert(msg) { console.log("createAlert('" + msg + "')"); // Create the promise for this alert, but don't start it yet let resolve, reject; const p = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); // Wait until the last alert is complete before starting this one lastPromise = lastPromise.then(() => { // Start this alert, and when it's done, resolve our chain promise // This is obviousyl VERY CRUDE, just for demo purposes console.log("showing '" + msg + "'"); function handler(e) { switch (e.target.value) { case "OK": console.log("user clicked OK on '" + msg + "'"); close(); resolve(); break; case "Cancel": console.log("user clicked Cancel on '" + msg + "'"); close(); reject(); break; } } function close() { alert.style.display = "none"; alert.querySelector(".message").innerHTML = ""; alert.removeEventListener("click", handler); } const alert = document.getElementById("alert"); alert.querySelector(".message").appendChild( document.createTextNode(msg) ); alert.addEventListener("click", handler); alert.style.display = ""; // Slave our chain to the resolution/rejection of the alert return p.catch(e => undefined).then(v => undefined); }); // Return the alert promise return p; } function test(msg) { createAlert(msg) .then(() => { console.log("alert resolved for '" + msg + "'"); }) .catch(() => { console.log("alert rejected for '" + msg + "'"); }); } test("one"); test("two"); test("three"); 
 .as-console-wrapper { max-height: 100% !important; } 
 <div id="alert" style="display: none"> <div class="message"></div> <input type="button" value="OK"> <input type="button" value="Cancel"> </div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM