Search code examples
javascriptjqueryasync-awaitjquery-deferred

How to extract a value from a promise when called synchronously using Javascript `await`?


I'm working on a project that uses a custom user prompt using jQuery / jQuery UI:

function dialog_confirm(dialog_body, dialog_title = 'Confirm') {
  var def = $.Deferred();
  
  $('<div></div>').dialog({
    modal: true,
    title: dialog_title,
    open: function () {
      $(this).html(dialog_body);
    },
    buttons: {
      OK: function () {
        $(this).dialog("close");
        def.resolve();
      },
      Cancel: function () {
        $(this).dialog("close");
        def.reject();
      }
    }
  });
    
  return def.promise();
}

I didn't write the above function, and I'm not able to make changes to it unless the changes are sure not to interfere with any other calls to this function elsewhere in the code.

I'm currently working on some functionality to save details of products, but prior to saving, some checks are performed:

save_product(current_product_details);

function save_product(product_details) {
    let price_verified = check_price(product_details['price']);
    if (!price_verified) {
      return;
    }

    // Proceed here to save product details to DB
}

function check_price(price) {
  var proceed = true;
  
  if (price == 0) {
    proceed = false;
    
    dialog_confirm('Price is zero. Save anyway?').done(function() {
      proceed = true;
    });
  }
  
  return proceed;
}

Now, the above code doesn't work, because if the price is 0, and the user is prompted to confirm, the check_price function doesn't wait for the user's response, it just returns false, and the calling function, save_product just returns without saving the product. The user may well click OK, but nothing happens in any case.

So I was investigating ways to make the code wait for the user's response and came across the concepts of async functions and await. MDN Web Docs says:

Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.

So I'm trying to refactor my code around that concept, but I can't quite figure out the return values and how to extract something meaningful from the function calls...

save_product(current_product_details);

async function save_product(product_details) {
    let price_verified = await check_price(product_details['price']);
    if (!price_verified) {
      return;
    }

    // Proceed here to save product details to DB
}

async function check_price(price) {
  if (price == 0) {
    try {
      let return_value = await dialog_confirm('Price is zero. Save anyway?');
      return return_value;
    } catch (error) {
      console.log('Do something in this case?');
    }
  } else {
    return true;
  }
}

I'm quite aware that my understanding has broken down somewhere - around promises and resolving/rejecting them - but I'd appreciate some pointers on how to get this working.


Solution

  • Are you getting undefined for your return_value? I'm not familiar with JQuery but I think since you have def.resolve(), this will return void (kinda like Promise.resolve()). And def.reject() will throw an Error (Kinda like Promise.reject()). So what you can do is:

    async function check_price(price) {
      if (price == 0) {
        try {
          await dialog_confirm('Price is zero. Save anyway?');
          return true;
        } catch (error) {
          return false;
        }
      } else {
        return true;
      }
    }