use crate::prelude::{Cell, Rc, RefCell, Vec, format, mem, vec};
use crate::error::JsError;
use crate::gc::{Gc, Guard};
use crate::interpreter::Interpreter;
use crate::value::{
CheapClone, ExoticObject, Guarded, JsFunction, JsObject, JsValue, PromiseAllSharedState,
PromiseHandler, PromiseRaceSharedState, PromiseState, PromiseStatus, PropertyKey,
};
pub fn init_promise_prototype(interp: &mut Interpreter) {
let proto = interp.promise_prototype.clone();
interp.register_method(&proto, "then", promise_then, 2);
interp.register_method(&proto, "catch", promise_catch, 1);
interp.register_method(&proto, "finally", promise_finally, 1);
}
pub fn create_promise_constructor(interp: &mut Interpreter) -> Gc<JsObject> {
let ctor = interp.create_native_function("Promise", promise_constructor, 1);
let proto_key = PropertyKey::String(interp.intern("prototype"));
ctor.borrow_mut()
.set_property(proto_key, JsValue::Object(interp.promise_prototype.clone()));
let constructor_key = PropertyKey::String(interp.intern("constructor"));
interp
.promise_prototype
.borrow_mut()
.set_property(constructor_key, JsValue::Object(ctor.clone()));
interp.register_method(&ctor, "resolve", promise_resolve_static, 1);
interp.register_method(&ctor, "reject", promise_reject_static, 1);
interp.register_method(&ctor, "all", promise_all, 1);
interp.register_method(&ctor, "race", promise_race, 1);
interp.register_method(&ctor, "allSettled", promise_allsettled, 1);
interp.register_method(&ctor, "any", promise_any, 1);
interp.register_species_getter(&ctor);
ctor
}
pub fn create_promise(interp: &mut Interpreter, guard: &Guard<JsObject>) -> Gc<JsObject> {
let state = Rc::new(RefCell::new(PromiseState {
status: PromiseStatus::Pending,
result: None,
handlers: Vec::new(),
order_id: None,
}));
let obj = interp.create_object(guard);
{
let mut o = obj.borrow_mut();
o.prototype = Some(interp.promise_prototype.cheap_clone());
o.exotic = ExoticObject::Promise(state);
}
obj
}
pub fn create_order_promise(
interp: &mut Interpreter,
guard: &Guard<JsObject>,
order_id: crate::OrderId,
) -> Gc<JsObject> {
let state = Rc::new(RefCell::new(PromiseState {
status: PromiseStatus::Pending,
result: None,
handlers: Vec::new(),
order_id: Some(order_id),
}));
let obj = interp.create_object(guard);
{
let mut o = obj.borrow_mut();
o.prototype = Some(interp.promise_prototype.cheap_clone());
o.exotic = ExoticObject::Promise(state);
}
interp
.order_promises
.entry(order_id)
.or_default()
.push(obj.cheap_clone());
obj
}
pub fn create_fulfilled_promise(
interp: &mut Interpreter,
guard: &Guard<JsObject>,
value: JsValue,
) -> Gc<JsObject> {
let _value_guard = interp.guard_value(&value);
let state = Rc::new(RefCell::new(PromiseState {
status: PromiseStatus::Fulfilled,
result: Some(value),
handlers: Vec::new(),
order_id: None,
}));
let obj = interp.create_object(guard);
{
let mut o = obj.borrow_mut();
o.prototype = Some(interp.promise_prototype.cheap_clone());
o.exotic = ExoticObject::Promise(state);
}
obj
}
pub fn create_rejected_promise(
interp: &mut Interpreter,
guard: &Guard<JsObject>,
reason: JsValue,
) -> Gc<JsObject> {
let _reason_guard = interp.guard_value(&reason);
let state = Rc::new(RefCell::new(PromiseState {
status: PromiseStatus::Rejected,
result: Some(reason),
handlers: Vec::new(),
order_id: None,
}));
let obj = interp.create_object(guard);
{
let mut o = obj.borrow_mut();
o.prototype = Some(interp.promise_prototype.cheap_clone());
o.exotic = ExoticObject::Promise(state);
}
obj
}
fn resolve_promise(
interp: &mut Interpreter,
promise: &Gc<JsObject>,
value: JsValue,
) -> Result<(), JsError> {
if let JsValue::Object(obj) = &value
&& let ExoticObject::Promise(state) = &obj.borrow().exotic
{
let state_ref = state.borrow();
match state_ref.status {
PromiseStatus::Pending => {
drop(state_ref);
let promise_clone = promise.cheap_clone();
let mut state_mut = state.borrow_mut();
state_mut.handlers.push(PromiseHandler {
on_fulfilled: None,
on_rejected: None,
result_promise: promise_clone,
});
return Ok(());
}
PromiseStatus::Fulfilled => {
let result = state_ref.result.clone().unwrap_or(JsValue::Undefined);
drop(state_ref);
return fulfill_promise(interp, promise, result);
}
PromiseStatus::Rejected => {
let result = state_ref.result.clone().unwrap_or(JsValue::Undefined);
drop(state_ref);
return reject_promise(interp, promise, result);
}
}
}
fulfill_promise(interp, promise, value)
}
fn fulfill_promise(
interp: &mut Interpreter,
promise: &Gc<JsObject>,
value: JsValue,
) -> Result<(), JsError> {
let handlers = {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
let mut state_mut = state.borrow_mut();
if state_mut.status != PromiseStatus::Pending {
return Ok(()); }
state_mut.status = PromiseStatus::Fulfilled;
state_mut.result = Some(value.clone());
mem::take(&mut state_mut.handlers)
};
for handler in handlers {
trigger_handler(interp, handler, &value, true)?;
}
Ok(())
}
fn reject_promise(
interp: &mut Interpreter,
promise: &Gc<JsObject>,
reason: JsValue,
) -> Result<(), JsError> {
let (handlers, order_id) = {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
let mut state_mut = state.borrow_mut();
if state_mut.status != PromiseStatus::Pending {
return Ok(()); }
state_mut.status = PromiseStatus::Rejected;
state_mut.result = Some(reason.clone());
let order_id = state_mut.order_id;
(mem::take(&mut state_mut.handlers), order_id)
};
if let Some(id) = order_id {
interp.cancelled_orders.push(id);
}
for handler in handlers {
trigger_handler(interp, handler, &reason, false)?;
}
Ok(())
}
pub fn resolve_promise_value(
interp: &mut Interpreter,
promise: &Gc<JsObject>,
value: JsValue,
) -> Result<(), JsError> {
resolve_promise(interp, promise, value)
}
pub fn reject_promise_value(
interp: &mut Interpreter,
promise: &Gc<JsObject>,
reason: JsValue,
) -> Result<(), JsError> {
reject_promise(interp, promise, reason)
}
fn trigger_handler(
interp: &mut Interpreter,
handler: PromiseHandler,
value: &JsValue,
is_fulfilled: bool,
) -> Result<(), JsError> {
let guard = interp.heap.create_guard();
guard.guard(handler.result_promise.clone());
if let Some(JsValue::Object(ref cb)) = handler.on_fulfilled {
guard.guard(cb.clone());
}
if let Some(JsValue::Object(ref cb)) = handler.on_rejected {
guard.guard(cb.clone());
}
let callback = if is_fulfilled {
handler.on_fulfilled.clone()
} else {
handler.on_rejected.clone()
};
match callback {
Some(cb) => {
let is_promise_all_handler = if let JsValue::Object(ref cb_obj) = cb {
let cb_ref = cb_obj.borrow();
matches!(
cb_ref.exotic,
ExoticObject::Function(JsFunction::PromiseAllFulfill { .. })
| ExoticObject::Function(JsFunction::PromiseAllReject(_))
)
} else {
false
};
match interp.call_function(cb, JsValue::Undefined, core::slice::from_ref(value)) {
Ok(Guarded { value: result, .. }) => {
if !is_promise_all_handler {
resolve_promise(interp, &handler.result_promise, result)?;
}
}
Err(e) => {
let error_value = e.to_value();
reject_promise(interp, &handler.result_promise, error_value)?;
}
}
}
None => {
if is_fulfilled {
fulfill_promise(interp, &handler.result_promise, value.clone())?;
} else {
reject_promise(interp, &handler.result_promise, value.clone())?;
}
}
}
Ok(())
}
pub fn promise_constructor(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let executor = args
.first()
.cloned()
.ok_or_else(|| JsError::type_error("Promise resolver undefined is not a function"))?;
if !executor.is_callable() {
let type_str = interp.type_of(&executor);
return Err(JsError::type_error(format!(
"Promise resolver {} is not a function",
type_str.as_str()
)));
}
let guard = interp.heap.create_guard();
let promise = create_promise(interp, &guard);
let resolve_fn =
interp.create_js_function(&guard, JsFunction::PromiseResolve(promise.cheap_clone()));
let reject_fn =
interp.create_js_function(&guard, JsFunction::PromiseReject(promise.cheap_clone()));
match interp.call_function(
executor,
JsValue::Undefined,
&[JsValue::Object(resolve_fn), JsValue::Object(reject_fn)],
) {
Ok(_) => {}
Err(e) => {
let error_value = e.to_value();
reject_promise(interp, &promise, error_value)?;
}
}
Ok(Guarded::with_guard(JsValue::Object(promise), guard))
}
pub fn promise_then(
interp: &mut Interpreter,
this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let JsValue::Object(obj) = this else {
return Err(JsError::type_error(
"Promise.prototype.then called on non-object",
));
};
let guard = interp.heap.create_guard();
guard.guard(obj.clone());
let promise = obj;
let on_fulfilled = args.first().cloned();
let on_rejected = args.get(1).cloned();
if let Some(JsValue::Object(ref cb)) = on_fulfilled {
guard.guard(cb.clone());
}
if let Some(JsValue::Object(ref cb)) = on_rejected {
guard.guard(cb.clone());
}
let on_fulfilled = on_fulfilled.filter(|v| v.is_callable());
let on_rejected = on_rejected.filter(|v| v.is_callable());
let result_promise = create_promise(interp, &guard);
let (status, result) = {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
let state_ref = state.borrow();
(state_ref.status.clone(), state_ref.result.clone())
};
match status {
PromiseStatus::Pending => {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
state.borrow_mut().handlers.push(PromiseHandler {
on_fulfilled,
on_rejected,
result_promise: result_promise.cheap_clone(),
});
}
PromiseStatus::Fulfilled => {
let value = result.unwrap_or(JsValue::Undefined);
let handler = PromiseHandler {
on_fulfilled,
on_rejected,
result_promise: result_promise.cheap_clone(),
};
trigger_handler(interp, handler, &value, true)?;
}
PromiseStatus::Rejected => {
let reason = result.unwrap_or(JsValue::Undefined);
let handler = PromiseHandler {
on_fulfilled,
on_rejected,
result_promise: result_promise.cheap_clone(),
};
trigger_handler(interp, handler, &reason, false)?;
}
}
Ok(Guarded::with_guard(JsValue::Object(result_promise), guard))
}
pub fn promise_catch(
interp: &mut Interpreter,
this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let on_rejected = args.first().cloned().unwrap_or(JsValue::Undefined);
promise_then(interp, this, &[JsValue::Undefined, on_rejected])
}
pub fn promise_finally(
interp: &mut Interpreter,
this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let JsValue::Object(obj) = this.clone() else {
return Err(JsError::type_error(
"Promise.prototype.finally called on non-object",
));
};
let promise = obj;
let on_finally = args.first().cloned();
let on_finally = on_finally.filter(|v| v.is_callable());
match on_finally {
Some(callback) => {
let guard = interp.heap.create_guard();
guard.guard(promise.clone());
let result_promise = create_promise(interp, &guard);
let (status, result) = {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
let state_ref = state.borrow();
(state_ref.status.clone(), state_ref.result.clone())
};
match status {
PromiseStatus::Pending => {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
state.borrow_mut().handlers.push(PromiseHandler {
on_fulfilled: Some(callback.clone()),
on_rejected: Some(callback),
result_promise: result_promise.cheap_clone(),
});
}
PromiseStatus::Fulfilled => {
let value = result.unwrap_or(JsValue::Undefined);
let _ = interp.call_function(callback, JsValue::Undefined, &[]);
fulfill_promise(interp, &result_promise, value)?;
}
PromiseStatus::Rejected => {
let reason = result.unwrap_or(JsValue::Undefined);
let _ = interp.call_function(callback, JsValue::Undefined, &[]);
reject_promise(interp, &result_promise, reason)?;
}
}
Ok(Guarded::with_guard(JsValue::Object(result_promise), guard))
}
None => {
promise_then(interp, this, &[JsValue::Undefined, JsValue::Undefined])
}
}
}
pub fn promise_resolve_static(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let value = args.first().cloned().unwrap_or(JsValue::Undefined);
if let JsValue::Object(obj) = &value
&& matches!(obj.borrow().exotic, ExoticObject::Promise(_))
{
return Ok(Guarded::unguarded(value));
}
let guard = interp.heap.create_guard();
let promise = create_fulfilled_promise(interp, &guard, value);
Ok(Guarded::with_guard(JsValue::Object(promise), guard))
}
pub fn resolve_promise_sync(
_interp: &mut Interpreter,
promise: &Gc<JsObject>,
) -> Result<JsValue, JsError> {
let obj = promise.borrow();
let ExoticObject::Promise(ref state) = obj.exotic else {
return Err(JsError::type_error("Not a promise"));
};
let state_ref = state.borrow();
match state_ref.status {
PromiseStatus::Fulfilled => Ok(state_ref.result.clone().unwrap_or(JsValue::Undefined)),
PromiseStatus::Rejected => {
let reason = state_ref.result.clone().unwrap_or(JsValue::Undefined);
let guarded = Guarded::from_value(reason, &_interp.heap);
Err(JsError::ThrownValue { guarded })
}
PromiseStatus::Pending => {
Err(JsError::internal_error(
"Cannot synchronously resolve pending promise in async generator delegation",
))
}
}
}
pub fn promise_reject_static(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let reason = args.first().cloned().unwrap_or(JsValue::Undefined);
let guard = interp.heap.create_guard();
let promise = create_rejected_promise(interp, &guard, reason);
Ok(Guarded::with_guard(JsValue::Object(promise), guard))
}
fn resolve_each_value(
interp: &mut Interpreter,
value: &JsValue,
_this: &JsValue,
) -> Result<(Vec<JsValue>, crate::gc::Guard<JsObject>), JsError> {
let raw_values = extract_iterable(value)?;
let guard = interp.heap.create_guard();
let mut results = Vec::with_capacity(raw_values.len());
for val in raw_values {
if let JsValue::Object(obj) = &val {
let obj_ref = obj.borrow();
if matches!(&obj_ref.exotic, ExoticObject::Promise(_)) {
drop(obj_ref);
guard.guard(obj.cheap_clone());
results.push(val);
continue;
}
if let ExoticObject::PendingOrder { id } = &obj_ref.exotic {
let order_id = crate::OrderId(*id);
drop(obj_ref);
let promise = create_order_promise(interp, &guard, order_id);
results.push(JsValue::Object(promise));
continue;
}
}
let promise = create_fulfilled_promise(interp, &guard, val);
results.push(JsValue::Object(promise));
}
Ok((results, guard))
}
fn extract_iterable(value: &JsValue) -> Result<Vec<JsValue>, JsError> {
let JsValue::Object(arr) = value else {
return Ok(vec![]);
};
let arr_ref = arr.borrow();
if let Some(elements) = arr_ref.array_elements() {
Ok(elements.to_vec())
} else {
Ok(vec![])
}
}
pub fn promise_all(
interp: &mut Interpreter,
this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let iterable = args.first().cloned().unwrap_or(JsValue::Undefined);
let (promises, _promises_guard) = resolve_each_value(interp, &iterable, &this)?;
let guard = interp.heap.create_guard();
if promises.is_empty() {
let arr = interp.create_empty_array(&guard);
let promise = create_fulfilled_promise(interp, &guard, JsValue::Object(arr));
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let mut results: Vec<JsValue> = vec![JsValue::Undefined; promises.len()];
let mut pending_count = 0;
let mut pending_indices: Vec<usize> = Vec::new();
for (i, promise_value) in promises.iter().enumerate() {
let (status, result) = if let JsValue::Object(obj) = promise_value {
let obj_ref = obj.borrow();
if let ExoticObject::Promise(ref state) = obj_ref.exotic {
let state_ref = state.borrow();
(state_ref.status.clone(), state_ref.result.clone())
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
}
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
};
match status {
PromiseStatus::Fulfilled => {
if let Some(idx) = results.get_mut(i) {
*idx = result.unwrap_or(JsValue::Undefined);
}
}
PromiseStatus::Rejected => {
let reason = result.unwrap_or(JsValue::Undefined);
let promise = create_rejected_promise(interp, &guard, reason);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
PromiseStatus::Pending => {
pending_count += 1;
pending_indices.push(i);
}
}
}
if pending_count == 0 {
let arr = interp.create_array_from(&guard, results);
let promise = create_fulfilled_promise(interp, &guard, JsValue::Object(arr));
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let result_promise = create_promise(interp, &guard);
let shared_state = Rc::new(PromiseAllSharedState {
remaining: Cell::new(pending_count),
results: RefCell::new(results),
result_promise: result_promise.cheap_clone(),
rejected: Cell::new(false),
});
for &idx in &pending_indices {
if let Some(JsValue::Object(promise_obj)) = promises.get(idx) {
let promise_obj_ref = promise_obj.borrow();
if let ExoticObject::Promise(ref state) = promise_obj_ref.exotic {
let on_fulfilled = interp.create_object(&guard);
{
let mut f = on_fulfilled.borrow_mut();
f.prototype = Some(interp.function_prototype.cheap_clone());
f.exotic = ExoticObject::Function(JsFunction::PromiseAllFulfill {
state: shared_state.clone(),
index: idx,
});
}
let on_rejected = interp.create_object(&guard);
{
let mut f = on_rejected.borrow_mut();
f.prototype = Some(interp.function_prototype.cheap_clone());
f.exotic =
ExoticObject::Function(JsFunction::PromiseAllReject(shared_state.clone()));
}
let mut state_mut = state.borrow_mut();
state_mut.handlers.push(PromiseHandler {
on_fulfilled: Some(JsValue::Object(on_fulfilled)),
on_rejected: Some(JsValue::Object(on_rejected)),
result_promise: result_promise.cheap_clone(),
});
}
}
}
Ok(Guarded::with_guard(JsValue::Object(result_promise), guard))
}
pub fn handle_promise_all_fulfill(
interp: &mut Interpreter,
state: &Rc<PromiseAllSharedState>,
index: usize,
value: JsValue,
) -> Result<(), JsError> {
if state.rejected.get() {
return Ok(());
}
{
let mut results = state.results.borrow_mut();
if let Some(slot) = results.get_mut(index) {
*slot = value;
}
}
let remaining = state.remaining.get();
state.remaining.set(remaining - 1);
if remaining - 1 == 0 {
let results = mem::take(&mut *state.results.borrow_mut());
let result_promise = state.result_promise.cheap_clone();
let guard = interp.heap.create_guard();
let arr = interp.create_array_from(&guard, results);
fulfill_promise(interp, &result_promise, JsValue::Object(arr))?;
}
Ok(())
}
pub fn handle_promise_all_reject(
interp: &mut Interpreter,
state: &Rc<PromiseAllSharedState>,
reason: JsValue,
) -> Result<(), JsError> {
if state.rejected.get() {
return Ok(());
}
state.rejected.set(true);
let result_promise = state.result_promise.cheap_clone();
reject_promise(interp, &result_promise, reason)?;
Ok(())
}
pub fn handle_promise_race_settle(
interp: &mut Interpreter,
state: &Rc<PromiseRaceSharedState>,
value: JsValue,
is_fulfill: bool,
winner_index: usize,
) -> Result<(), JsError> {
if state.settled.get() {
return Ok(());
}
state.settled.set(true);
for (i, order_id) in state.input_order_ids.iter().enumerate() {
if i != winner_index
&& let Some(id) = order_id
{
interp.cancelled_orders.push(*id);
}
}
let result_promise = state.result_promise.cheap_clone();
if is_fulfill {
fulfill_promise(interp, &result_promise, value)?;
} else {
reject_promise(interp, &result_promise, value)?;
}
Ok(())
}
pub fn promise_race(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
use crate::value::PromiseRaceSharedState;
let iterable = args.first().cloned().unwrap_or(JsValue::Undefined);
let promises = extract_iterable(&iterable)?;
let guard = interp.heap.create_guard();
if promises.is_empty() {
let promise = create_promise(interp, &guard);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let mut pending_promises: Vec<(Gc<JsObject>, Option<crate::OrderId>)> = Vec::new();
for promise_value in &promises {
let (status, result, order_id) = if let JsValue::Object(obj) = promise_value {
let obj_ref = obj.borrow();
if let ExoticObject::Promise(ref state) = obj_ref.exotic {
let state_ref = state.borrow();
(
state_ref.status.clone(),
state_ref.result.clone(),
state_ref.order_id,
)
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()), None)
}
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()), None)
};
match status {
PromiseStatus::Fulfilled => {
let value = result.unwrap_or(JsValue::Undefined);
let promise = create_fulfilled_promise(interp, &guard, value);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
PromiseStatus::Rejected => {
let reason = result.unwrap_or(JsValue::Undefined);
let promise = create_rejected_promise(interp, &guard, reason);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
PromiseStatus::Pending => {
if let JsValue::Object(obj) = promise_value {
pending_promises.push((obj.cheap_clone(), order_id));
}
}
}
}
let result_promise = create_promise(interp, &guard);
let input_order_ids: Vec<Option<crate::OrderId>> =
pending_promises.iter().map(|(_, id)| *id).collect();
let shared_state = Rc::new(PromiseRaceSharedState {
result_promise: result_promise.cheap_clone(),
settled: Cell::new(false),
input_order_ids,
});
for (index, (promise_obj, _)) in pending_promises.iter().enumerate() {
let promise_obj_ref = promise_obj.borrow();
if let ExoticObject::Promise(ref state) = promise_obj_ref.exotic {
let on_fulfilled = interp.create_object(&guard);
{
let mut f = on_fulfilled.borrow_mut();
f.prototype = Some(interp.function_prototype.cheap_clone());
f.exotic = ExoticObject::Function(JsFunction::PromiseRaceSettle {
state: shared_state.clone(),
is_fulfill: true,
index,
});
}
let on_rejected = interp.create_object(&guard);
{
let mut f = on_rejected.borrow_mut();
f.prototype = Some(interp.function_prototype.cheap_clone());
f.exotic = ExoticObject::Function(JsFunction::PromiseRaceSettle {
state: shared_state.clone(),
is_fulfill: false,
index,
});
}
let mut state_mut = state.borrow_mut();
state_mut.handlers.push(PromiseHandler {
on_fulfilled: Some(JsValue::Object(on_fulfilled)),
on_rejected: Some(JsValue::Object(on_rejected)),
result_promise: result_promise.cheap_clone(),
});
}
}
Ok(Guarded::with_guard(JsValue::Object(result_promise), guard))
}
pub fn promise_allsettled(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let iterable = args.first().cloned().unwrap_or(JsValue::Undefined);
let promises = extract_iterable(&iterable)?;
let guard = interp.heap.create_guard();
if promises.is_empty() {
let arr = interp.create_empty_array(&guard);
let promise = create_fulfilled_promise(interp, &guard, JsValue::Object(arr));
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let status_key = PropertyKey::String(interp.intern("status"));
let value_key = PropertyKey::String(interp.intern("value"));
let reason_key = PropertyKey::String(interp.intern("reason"));
let mut results: Vec<JsValue> = Vec::with_capacity(promises.len());
for promise_value in &promises {
let (status, result) = if let JsValue::Object(obj) = promise_value {
let obj_ref = obj.borrow();
if let ExoticObject::Promise(ref state) = obj_ref.exotic {
let state_ref = state.borrow();
(state_ref.status.clone(), state_ref.result.clone())
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
}
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
};
let result_obj = interp.create_object(&guard);
{
let mut result_ref = result_obj.borrow_mut();
result_ref.prototype = Some(interp.object_prototype.cheap_clone());
match status {
PromiseStatus::Fulfilled => {
result_ref
.set_property(status_key.clone(), JsValue::String("fulfilled".into()));
result_ref
.set_property(value_key.clone(), result.unwrap_or(JsValue::Undefined));
}
PromiseStatus::Rejected => {
result_ref.set_property(status_key.clone(), JsValue::String("rejected".into()));
result_ref
.set_property(reason_key.clone(), result.unwrap_or(JsValue::Undefined));
}
PromiseStatus::Pending => {
result_ref.set_property(status_key.clone(), JsValue::String("pending".into()));
}
}
}
results.push(JsValue::Object(result_obj));
}
let arr = interp.create_array_from(&guard, results);
let promise = create_fulfilled_promise(interp, &guard, JsValue::Object(arr));
Ok(Guarded::with_guard(JsValue::Object(promise), guard))
}
pub fn promise_any(
interp: &mut Interpreter,
_this: JsValue,
args: &[JsValue],
) -> Result<Guarded, JsError> {
let iterable = args.first().cloned().unwrap_or(JsValue::Undefined);
let promises = extract_iterable(&iterable)?;
let guard = interp.heap.create_guard();
if promises.is_empty() {
let promise = create_rejected_promise(
interp,
&guard,
JsValue::String("All promises were rejected".into()),
);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let mut errors: Vec<JsValue> = Vec::new();
let mut fulfilled_value: Option<JsValue> = None;
let mut any_pending = false;
for promise_value in &promises {
let (status, result) = if let JsValue::Object(obj) = promise_value {
let obj_ref = obj.borrow();
if let ExoticObject::Promise(ref state) = obj_ref.exotic {
let state_ref = state.borrow();
(state_ref.status.clone(), state_ref.result.clone())
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
}
} else {
(PromiseStatus::Fulfilled, Some(promise_value.clone()))
};
match status {
PromiseStatus::Fulfilled => {
fulfilled_value = Some(result.unwrap_or(JsValue::Undefined));
break;
}
PromiseStatus::Rejected => {
errors.push(result.unwrap_or(JsValue::Undefined));
}
PromiseStatus::Pending => {
any_pending = true;
}
}
}
if let Some(value) = fulfilled_value {
let promise = create_fulfilled_promise(interp, &guard, value);
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
if !errors.is_empty() && !any_pending {
let errors_arr = interp.create_array_from(&guard, errors);
let promise = create_rejected_promise(interp, &guard, JsValue::Object(errors_arr));
return Ok(Guarded::with_guard(JsValue::Object(promise), guard));
}
let promise = create_promise(interp, &guard);
Ok(Guarded::with_guard(JsValue::Object(promise), guard))
}