mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
JSO: add interface for Promise (#884)
This commit is contained in:
parent
be09e698ea
commit
6788642ea9
124
jso/apis/src/main/java/org/teavm/jso/core/JSPromise.java
Normal file
124
jso/apis/src/main/java/org/teavm/jso/core/JSPromise.java
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.core;
|
||||||
|
|
||||||
|
import org.teavm.interop.NoSideEffects;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.JSFunctor;
|
||||||
|
import org.teavm.jso.JSMethod;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
import org.teavm.jso.JSProperty;
|
||||||
|
import org.teavm.jso.util.function.JSConsumer;
|
||||||
|
import org.teavm.jso.util.function.JSFunction;
|
||||||
|
import org.teavm.jso.util.function.JSSupplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for interacting with JavaScript
|
||||||
|
* <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">
|
||||||
|
* {@code Promise}s</a>.
|
||||||
|
*
|
||||||
|
* @param <T> The type this promise returns when resolving successfully.
|
||||||
|
*/
|
||||||
|
public abstract class JSPromise<T> implements JSObject {
|
||||||
|
private JSPromise() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Interface for a function wrapped by a promise. */
|
||||||
|
@FunctionalInterface
|
||||||
|
@JSFunctor
|
||||||
|
public interface Executor<T> extends JSObject {
|
||||||
|
/**
|
||||||
|
* @param resolveFunc Call this function to resolve with success value.
|
||||||
|
* @param rejectFunc Call this function to reject with error value.
|
||||||
|
*/
|
||||||
|
void onExecute(JSConsumer<T> resolveFunc, JSConsumer<Object> rejectFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "executor", script = "return new Promise(executor);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <T> JSPromise<T> create(Executor<T> executor);
|
||||||
|
|
||||||
|
@JSBody(params = "promises", script = "return Promise.any(promises);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<V> any(JSArrayReader<JSPromise<V>> promises);
|
||||||
|
|
||||||
|
// TODO: Allow passing differently typed promises via a JSTuple<T1, ...> interface
|
||||||
|
@JSBody(params = "promises", script = "return Promise.all(promises);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<JSArrayReader<V>> all(JSArrayReader<JSPromise<V>> promises);
|
||||||
|
|
||||||
|
// TODO: Allow passing differently typed promises via a JSTuple<T1, ...> interface
|
||||||
|
@JSBody(params = "promises", script = "return Promise.allSettled(promises);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<JSArrayReader<FulfillmentValue<V>>>
|
||||||
|
allSettled(JSArrayReader<JSPromise<V>> promises);
|
||||||
|
|
||||||
|
@JSBody(params = "promises", script = "return Promise.race(promises);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<V> race(JSArrayReader<JSPromise<V>> promises);
|
||||||
|
|
||||||
|
@JSBody(params = "value", script = "return Promise.resolve(value);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<V> resolve(V value);
|
||||||
|
|
||||||
|
@JSBody(params = "reason", script = "return Promise.reject(reason);")
|
||||||
|
@NoSideEffects
|
||||||
|
public static native <V> JSPromise<V> reject(Object reason);
|
||||||
|
|
||||||
|
/** Call {@code onFulfilled} with the success value, resolving with its return value. */
|
||||||
|
public abstract <V> JSPromise<V> then(JSFunction<T, V> onFulfilled);
|
||||||
|
|
||||||
|
/** Call {@code onFulfilled} with the success value or {@code onRejected} with the reject reason,
|
||||||
|
* resolving with its return value. */
|
||||||
|
public abstract <V> JSPromise<V> then(JSFunction<T, V> onFulfilled, JSFunction<Object, V> onRejected);
|
||||||
|
|
||||||
|
/** Call {@code onFulfilled} with the success value, returning a new promise. */
|
||||||
|
@JSMethod("then")
|
||||||
|
public abstract <V> JSPromise<V> flatThen(JSFunction<T, ? extends JSPromise<V>> onFulfilled);
|
||||||
|
|
||||||
|
/** Call {@code onFulfilled} with the success value or {@code onRejected} with the reject reason,
|
||||||
|
* returning a new promise. */
|
||||||
|
@JSMethod("then")
|
||||||
|
public abstract <V> JSPromise<V> flatThen(JSFunction<T, ? extends JSPromise<V>> onFulfilled,
|
||||||
|
JSFunction<Object, ? extends JSPromise<V>> onRejected);
|
||||||
|
|
||||||
|
/** Call {@code onRejected} with the reject reason, resolving with its return value. */
|
||||||
|
@JSMethod("catch")
|
||||||
|
public abstract <V> JSPromise<V> catchError(JSFunction<Object, V> onRejected);
|
||||||
|
|
||||||
|
/** Call {@code onRejected} with the reject reason, returning a new promise. */
|
||||||
|
@JSMethod("catch")
|
||||||
|
public abstract <V> JSPromise<V> flatCatchError(JSFunction<Object, ? extends JSPromise<V>> onRejected);
|
||||||
|
|
||||||
|
/** Call {@code onFinally} after settling, ignoring the return value. */
|
||||||
|
@JSMethod("finally")
|
||||||
|
public abstract JSPromise<T> onSettled(JSSupplier<Object> onFinally);
|
||||||
|
|
||||||
|
/** Interface for the return values of {@ref #allSettled()}. */
|
||||||
|
public interface FulfillmentValue<T> extends JSObject {
|
||||||
|
@JSProperty
|
||||||
|
@NoSideEffects
|
||||||
|
JSString getStatus();
|
||||||
|
|
||||||
|
@JSProperty
|
||||||
|
@NoSideEffects
|
||||||
|
T getValue();
|
||||||
|
|
||||||
|
@JSProperty
|
||||||
|
@NoSideEffects
|
||||||
|
Object getReason();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.util.function;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.teavm.jso.JSFunctor;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
@JSFunctor
|
||||||
|
public interface JSConsumer<T> extends JSObject {
|
||||||
|
void accept(T t);
|
||||||
|
|
||||||
|
default JSConsumer<T> andThen(JSConsumer<? super T> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
|
||||||
|
return (T t) -> {
|
||||||
|
this.accept(t);
|
||||||
|
after.accept(t);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.util.function;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.teavm.jso.JSFunctor;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
@JSFunctor
|
||||||
|
public interface JSFunction<T, R> extends JSObject {
|
||||||
|
R apply(T t);
|
||||||
|
|
||||||
|
default <V> JSFunction<T, V> andThen(JSFunction<? super R, ? extends V> after) {
|
||||||
|
Objects.requireNonNull(after);
|
||||||
|
|
||||||
|
return (T t) -> {
|
||||||
|
R result = this.apply(t);
|
||||||
|
return after.apply(result);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default <V> JSFunction<V, R> compose(JSFunction<? super V, ? extends T> before) {
|
||||||
|
Objects.requireNonNull(before);
|
||||||
|
|
||||||
|
return (V v) -> {
|
||||||
|
T result = before.apply(v);
|
||||||
|
return this.apply(result);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> JSFunction<T, T> identity() {
|
||||||
|
return (T t) -> {
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.jso.util.function;
|
||||||
|
|
||||||
|
import org.teavm.jso.JSFunctor;
|
||||||
|
import org.teavm.jso.JSObject;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
@JSFunctor
|
||||||
|
public interface JSSupplier<R> extends JSObject {
|
||||||
|
R get();
|
||||||
|
}
|
41
samples/promise/build.gradle.kts
Normal file
41
samples/promise/build.gradle.kts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
war
|
||||||
|
id("org.teavm")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
create("war")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
teavm(teavm.libs.jsoApis)
|
||||||
|
"war"(project(":stdout-helper", "war"))
|
||||||
|
}
|
||||||
|
|
||||||
|
teavm.js {
|
||||||
|
addedToWebApp = true
|
||||||
|
obfuscated = false
|
||||||
|
mainClass = "org.teavm.samples.promise.PromiseExample"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.war {
|
||||||
|
dependsOn(configurations["war"])
|
||||||
|
from(provider { configurations["war"].map { zipTree(it) } })
|
||||||
|
}
|
|
@ -0,0 +1,423 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Bernd Busse.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.samples.promise;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.browser.Window;
|
||||||
|
import org.teavm.jso.core.JSArray;
|
||||||
|
import org.teavm.jso.core.JSPromise;
|
||||||
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.jso.util.function.JSConsumer;
|
||||||
|
import org.teavm.jso.util.function.JSFunction;
|
||||||
|
import org.teavm.jso.util.function.JSSupplier;
|
||||||
|
|
||||||
|
public final class PromiseExample {
|
||||||
|
private static long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
private PromiseExample() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws InterruptedException {
|
||||||
|
report(Arrays.toString(args));
|
||||||
|
report("");
|
||||||
|
|
||||||
|
checkFunctionalInterface();
|
||||||
|
|
||||||
|
runSimplePromise();
|
||||||
|
|
||||||
|
runComplexPromise();
|
||||||
|
|
||||||
|
final var lock = new Object();
|
||||||
|
runLongRunningPromise(lock);
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
combinePromises(lock);
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
testNativePromises(lock);
|
||||||
|
|
||||||
|
report("Finished main thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void report(String message) {
|
||||||
|
var current = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkFunctionalInterface() {
|
||||||
|
JSSupplier<Integer> supplier = () -> 23;
|
||||||
|
|
||||||
|
JSFunction<Integer, Integer> addTwenty = value -> value + 20;
|
||||||
|
JSFunction<Integer, Integer> subTwenty = value -> value - 20;
|
||||||
|
JSFunction<Integer, Boolean> isPositive = value -> value >= 0;
|
||||||
|
|
||||||
|
JSConsumer<Integer> print = value -> report("My value: " + value.toString());
|
||||||
|
JSConsumer<Integer> print2 = value -> report("My value plus 10: " + Integer.valueOf(value + 10).toString());
|
||||||
|
|
||||||
|
var value = supplier.get();
|
||||||
|
report("Supplied value: " + value.toString());
|
||||||
|
|
||||||
|
value = addTwenty.apply(value);
|
||||||
|
report("Value plus 20: " + value.toString());
|
||||||
|
|
||||||
|
value = subTwenty.apply(value);
|
||||||
|
report("Value minus 20: " + value.toString());
|
||||||
|
|
||||||
|
var value2 = isPositive.apply(value);
|
||||||
|
report("Value is positive: " + value2.toString());
|
||||||
|
|
||||||
|
var subFourty = subTwenty.andThen(subTwenty);
|
||||||
|
value = subFourty.apply(value);
|
||||||
|
report("Value minus 40: " + value.toString());
|
||||||
|
|
||||||
|
var plusFourty = addTwenty.compose(addTwenty).andThen(addTwenty.compose(subTwenty));
|
||||||
|
value = plusFourty.apply(value);
|
||||||
|
report("Value plus 40: " + value.toString());
|
||||||
|
|
||||||
|
value2 = subFourty.andThen(isPositive).apply(value);
|
||||||
|
report("Value minus 40 is positive: " + value2.toString());
|
||||||
|
|
||||||
|
print.accept(value);
|
||||||
|
var printExtended = print.andThen(print2);
|
||||||
|
printExtended.accept(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runSimplePromise() {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Simple promise execution");
|
||||||
|
|
||||||
|
report("Resolving with 'success'");
|
||||||
|
resolve.accept("success");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runComplexPromise() {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Complex promise execution");
|
||||||
|
|
||||||
|
report("Resolving with 'step1'");
|
||||||
|
resolve.accept("step1");
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with 'step2'");
|
||||||
|
return "step2";
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and throw exception");
|
||||||
|
throw new RuntimeException("Exception in promise handler");
|
||||||
|
}, reason -> {
|
||||||
|
report("Failed unexpectedly with reason: " + reason.toString());
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Failed expectedly with reason: " + reason.toString());
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with resolved promise");
|
||||||
|
return JSPromise.resolve("step3");
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with rejected promise");
|
||||||
|
return JSPromise.reject("step4");
|
||||||
|
})
|
||||||
|
.catchError(reason -> {
|
||||||
|
report("Catched reject reason '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with new promise");
|
||||||
|
return JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Inner promise");
|
||||||
|
report("Reject with 'step from inner'");
|
||||||
|
reject.accept("step from inner");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
})
|
||||||
|
.catchError(reason -> {
|
||||||
|
report("Catched reject reason '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.onSettled(() -> {
|
||||||
|
report("Promise has finally settled");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runLongRunningPromise(Object lock) {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Long promise exection");
|
||||||
|
report("Wait for a while...");
|
||||||
|
Window.setTimeout(() -> {
|
||||||
|
report("... and resolve with 'done'");
|
||||||
|
resolve.accept("done");
|
||||||
|
}, 2000);
|
||||||
|
}).then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void combinePromises(Object lock) throws InterruptedException {
|
||||||
|
JSArray<JSPromise<String>> promises = JSArray.create(3);
|
||||||
|
|
||||||
|
report("Start 3 successful promises");
|
||||||
|
promises.set(0, JSPromise.resolve("success1"));
|
||||||
|
promises.set(1, JSPromise.resolve("success2"));
|
||||||
|
promises.set(2, JSPromise.resolve("success3"));
|
||||||
|
|
||||||
|
var allPromises = JSPromise.all(promises);
|
||||||
|
allPromises.then(value -> {
|
||||||
|
report("All promises resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("At least one promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 1 successful and 2 rejected promise");
|
||||||
|
promises.set(0, JSPromise.resolve("success1"));
|
||||||
|
promises.set(1, JSPromise.reject("failure2"));
|
||||||
|
promises.set(2, JSPromise.reject("failure3"));
|
||||||
|
|
||||||
|
allPromises = JSPromise.all(promises);
|
||||||
|
allPromises.then(value -> {
|
||||||
|
report("All promises resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("At least one promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var settledPromises = JSPromise.allSettled(promises);
|
||||||
|
settledPromises.then(value -> {
|
||||||
|
report(Integer.toString(value.getLength()) + " promises settled to:");
|
||||||
|
for (int i = 0; i < value.getLength(); ++i) {
|
||||||
|
var item = value.get(i);
|
||||||
|
var msg = "-- Promise " + i + " " + item.getStatus() + " with: ";
|
||||||
|
if (item.getStatus().stringValue().equals("fulfilled")) {
|
||||||
|
msg += item.getValue().toString();
|
||||||
|
} else if (item.getStatus().stringValue().equals("rejected")) {
|
||||||
|
msg += item.getReason().toString();
|
||||||
|
}
|
||||||
|
report(msg);
|
||||||
|
}
|
||||||
|
return "success";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var anyPromise = JSPromise.any(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("At least one promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("All promises rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 rejected promises");
|
||||||
|
promises.set(0, JSPromise.reject("failure1"));
|
||||||
|
promises.set(1, JSPromise.reject("failure2"));
|
||||||
|
promises.set(2, JSPromise.reject("failure3"));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.any(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("At least one promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("All promises rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 delayed promises");
|
||||||
|
promises.set(0, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success1"), 200)));
|
||||||
|
promises.set(1, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> reject.accept("failure1"), 100)));
|
||||||
|
promises.set(2, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success3"), 50)));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.race(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("First settled promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("First settled promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 delayed promises");
|
||||||
|
promises.set(0, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success1"), 200)));
|
||||||
|
promises.set(1, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> reject.accept("failure1"), 50)));
|
||||||
|
promises.set(2, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success3"), 100)));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.race(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("First settled promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("First settled promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testNativePromises(Object lock) throws InterruptedException {
|
||||||
|
report("Get promise from native method");
|
||||||
|
var nativePromise = getNativePromise(JSString.valueOf("success from native"));
|
||||||
|
nativePromise.then(value -> {
|
||||||
|
report("Native resolved expectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Native rejected unexpectedly with '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
nativePromise = getNativeRejectingPromise(JSString.valueOf("failure from native"));
|
||||||
|
nativePromise.then(value -> {
|
||||||
|
report("Native resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Native rejected expectedly with '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Pass promise to native method");
|
||||||
|
handlePromise(JSPromise.create((resolve, reject) -> {
|
||||||
|
resolve.accept(JSString.valueOf("Resolved from Java"));
|
||||||
|
}));
|
||||||
|
|
||||||
|
handlePromise(JSPromise.create((resolve, reject) -> {
|
||||||
|
reject.accept(JSString.valueOf("Rejected from Java"));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "msg", script = "return new Promise((resolve, reject) => {"
|
||||||
|
+ " setTimeout(() => resolve(msg), 500);"
|
||||||
|
+ "});")
|
||||||
|
private static native JSPromise<JSString> getNativePromise(JSString msg);
|
||||||
|
|
||||||
|
@JSBody(params = "msg", script = "return new Promise((resolve, reject) => {"
|
||||||
|
+ " setTimeout(() => reject(msg), 500);"
|
||||||
|
+ "});")
|
||||||
|
private static native JSPromise<JSString> getNativeRejectingPromise(JSString msg);
|
||||||
|
|
||||||
|
@JSBody(params = "promise", script = "promise.then("
|
||||||
|
+ " (value) => console.log('success:', value),"
|
||||||
|
+ " (reason) => console.log('failure:', reason));")
|
||||||
|
private static native void handlePromise(JSPromise<JSString> promise);
|
||||||
|
}
|
21
samples/promise/src/main/webapp/WEB-INF/web.xml
Normal file
21
samples/promise/src/main/webapp/WEB-INF/web.xml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2015 Alexey Andreev.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
|
version="3.0">
|
||||||
|
</web-app>
|
1
samples/promise/src/main/webapp/highlight.pack.js
Normal file
1
samples/promise/src/main/webapp/highlight.pack.js
Normal file
File diff suppressed because one or more lines are too long
459
samples/promise/src/main/webapp/index.html
Normal file
459
samples/promise/src/main/webapp/index.html
Normal file
|
@ -0,0 +1,459 @@
|
||||||
|
<!--
|
||||||
|
Copyright 2015 Alexey Andreev.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Continuation-passing style demo</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||||
|
<script type="text/javascript" charset="utf-8" src="teavm/stdout.js"></script>
|
||||||
|
<script type="text/javascript" charset="utf-8" src="js/promise.js"></script>
|
||||||
|
<script type="text/javascript" charset="utf-8" src="highlight.pack.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="syntax.css">
|
||||||
|
<script type="text/javascript">
|
||||||
|
function runAll() {
|
||||||
|
hljs.highlightBlock(document.getElementById("source-code"));
|
||||||
|
main(["foo", "bar"]);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="runAll()">
|
||||||
|
<div id="description">This application shows how TeaVM can interact with JavaScript
|
||||||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"><code>Promise</code>s</a>
|
||||||
|
(see <a href="https://github.com/konsoletyper/teavm/tree/master/samples/promise">source code on GitHub</a>).</div>
|
||||||
|
|
||||||
|
<div id="blocks">
|
||||||
|
<div class="block" id="stdout-wrapper">
|
||||||
|
<div class="block-title">stdout</div>
|
||||||
|
<div class="block-content" id="stdout"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="block" id="source-wrapper">
|
||||||
|
<div class="block-title">Source code</div>
|
||||||
|
<pre class="block-content" id="source-code">
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.jso.JSBody;
|
||||||
|
import org.teavm.jso.browser.Window;
|
||||||
|
import org.teavm.jso.core.JSArray;
|
||||||
|
import org.teavm.jso.core.JSPromise;
|
||||||
|
import org.teavm.jso.core.JSString;
|
||||||
|
import org.teavm.jso.util.function.JSConsumer;
|
||||||
|
import org.teavm.jso.util.function.JSFunction;
|
||||||
|
import org.teavm.jso.util.function.JSSupplier;
|
||||||
|
|
||||||
|
public final class PromiseExample {
|
||||||
|
private static long start = System.currentTimeMillis();
|
||||||
|
|
||||||
|
private PromiseExample() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws InterruptedException {
|
||||||
|
report(Arrays.toString(args));
|
||||||
|
report("");
|
||||||
|
|
||||||
|
checkFunctionalInterface();
|
||||||
|
|
||||||
|
runSimplePromise();
|
||||||
|
|
||||||
|
runComplexPromise();
|
||||||
|
|
||||||
|
final var lock = new Object();
|
||||||
|
runLongRunningPromise(lock);
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
combinePromises(lock);
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
testNativePromises(lock);
|
||||||
|
|
||||||
|
report("Finished main thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void report(String message) {
|
||||||
|
var current = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkFunctionalInterface() {
|
||||||
|
JSSupplier<Integer> supplier = () -> 23;
|
||||||
|
|
||||||
|
JSFunction<Integer, Integer> addTwenty = value -> value + 20;
|
||||||
|
JSFunction<Integer, Integer> subTwenty = value -> value - 20;
|
||||||
|
JSFunction<Integer, Boolean> isPositive = value -> value >= 0;
|
||||||
|
|
||||||
|
JSConsumer<Integer> print = value -> report("My value: " + value.toString());
|
||||||
|
JSConsumer<Integer> print2 = value -> report("My value plus 10: " + Integer.valueOf(value + 10).toString());
|
||||||
|
|
||||||
|
var value = supplier.get();
|
||||||
|
report("Supplied value: " + value.toString());
|
||||||
|
|
||||||
|
value = addTwenty.apply(value);
|
||||||
|
report("Value plus 20: " + value.toString());
|
||||||
|
|
||||||
|
value = subTwenty.apply(value);
|
||||||
|
report("Value minus 20: " + value.toString());
|
||||||
|
|
||||||
|
var value2 = isPositive.apply(value);
|
||||||
|
report("Value is positive: " + value2.toString());
|
||||||
|
|
||||||
|
var subFourty = subTwenty.andThen(subTwenty);
|
||||||
|
value = subFourty.apply(value);
|
||||||
|
report("Value minus 40: " + value.toString());
|
||||||
|
|
||||||
|
var plusFourty = addTwenty.compose(addTwenty).andThen(addTwenty.compose(subTwenty));
|
||||||
|
value = plusFourty.apply(value);
|
||||||
|
report("Value plus 40: " + value.toString());
|
||||||
|
|
||||||
|
value2 = subFourty.andThen(isPositive).apply(value);
|
||||||
|
report("Value minus 40 is positive: " + value2.toString());
|
||||||
|
|
||||||
|
print.accept(value);
|
||||||
|
var printExtended = print.andThen(print2);
|
||||||
|
printExtended.accept(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runSimplePromise() {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Simple promise execution");
|
||||||
|
|
||||||
|
report("Resolving with 'success'");
|
||||||
|
resolve.accept("success");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runComplexPromise() {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Complex promise execution");
|
||||||
|
|
||||||
|
report("Resolving with 'step1'");
|
||||||
|
resolve.accept("step1");
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with 'step2'");
|
||||||
|
return "step2";
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and throw exception");
|
||||||
|
throw new RuntimeException("Exception in promise handler");
|
||||||
|
}, reason -> {
|
||||||
|
report("Failed unexpectedly with reason: " + reason.toString());
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Failed expectedly with reason: " + reason.toString());
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with resolved promise");
|
||||||
|
return JSPromise.resolve("step3");
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with rejected promise");
|
||||||
|
return JSPromise.reject("step4");
|
||||||
|
})
|
||||||
|
.catchError(reason -> {
|
||||||
|
report("Catched reject reason '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.flatThen(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
report("... and resolve with new promise");
|
||||||
|
return JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Inner promise");
|
||||||
|
report("Reject with 'step from inner'");
|
||||||
|
reject.accept("step from inner");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(value -> {
|
||||||
|
report("Resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
})
|
||||||
|
.catchError(reason -> {
|
||||||
|
report("Catched reject reason '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
})
|
||||||
|
.onSettled(() -> {
|
||||||
|
report("Promise has finally settled");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runLongRunningPromise(Object lock) {
|
||||||
|
var promise = JSPromise.create((resolve, reject) -> {
|
||||||
|
report("Long promise exection");
|
||||||
|
report("Wait for a while...");
|
||||||
|
Window.setTimeout(() -> {
|
||||||
|
report("... and resolve with 'done'");
|
||||||
|
resolve.accept("done");
|
||||||
|
}, 2000);
|
||||||
|
}).then(value -> {
|
||||||
|
report("Resolved with '" + value + "'");
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void combinePromises(Object lock) throws InterruptedException {
|
||||||
|
JSArray<JSPromise<String>> promises = JSArray.create(3);
|
||||||
|
|
||||||
|
report("Start 3 successful promises");
|
||||||
|
promises.set(0, JSPromise.resolve("success1"));
|
||||||
|
promises.set(1, JSPromise.resolve("success2"));
|
||||||
|
promises.set(2, JSPromise.resolve("success3"));
|
||||||
|
|
||||||
|
var allPromises = JSPromise.all(promises);
|
||||||
|
allPromises.then(value -> {
|
||||||
|
report("All promises resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("At least one promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 1 successful and 2 rejected promise");
|
||||||
|
promises.set(0, JSPromise.resolve("success1"));
|
||||||
|
promises.set(1, JSPromise.reject("failure2"));
|
||||||
|
promises.set(2, JSPromise.reject("failure3"));
|
||||||
|
|
||||||
|
allPromises = JSPromise.all(promises);
|
||||||
|
allPromises.then(value -> {
|
||||||
|
report("All promises resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("At least one promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var settledPromises = JSPromise.allSettled(promises);
|
||||||
|
settledPromises.then(value -> {
|
||||||
|
report(Integer.toString(value.getLength()) + " promises settled to:");
|
||||||
|
for (int i = 0; i < value.getLength(); ++i) {
|
||||||
|
var item = value.get(i);
|
||||||
|
var msg = "-- Promise " + i + " " + item.getStatus() + " with: ";
|
||||||
|
if (item.getStatus().stringValue().equals("fulfilled")) {
|
||||||
|
msg += item.getValue().toString();
|
||||||
|
} else if (item.getStatus().stringValue().equals("rejected")) {
|
||||||
|
msg += item.getReason().toString();
|
||||||
|
}
|
||||||
|
report(msg);
|
||||||
|
}
|
||||||
|
return "success";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var anyPromise = JSPromise.any(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("At least one promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("All promises rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 rejected promises");
|
||||||
|
promises.set(0, JSPromise.reject("failure1"));
|
||||||
|
promises.set(1, JSPromise.reject("failure2"));
|
||||||
|
promises.set(2, JSPromise.reject("failure3"));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.any(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("At least one promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("All promises rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 delayed promises");
|
||||||
|
promises.set(0, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success1"), 200)));
|
||||||
|
promises.set(1, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> reject.accept("failure1"), 100)));
|
||||||
|
promises.set(2, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success3"), 50)));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.race(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("First settled promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("First settled promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Start 3 delayed promises");
|
||||||
|
promises.set(0, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success1"), 200)));
|
||||||
|
promises.set(1, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> reject.accept("failure1"), 50)));
|
||||||
|
promises.set(2, JSPromise.create((resolve, reject) -> Window.setTimeout(() -> resolve.accept("success3"), 100)));
|
||||||
|
|
||||||
|
anyPromise = JSPromise.race(promises);
|
||||||
|
anyPromise.then(value -> {
|
||||||
|
report("First settled promise resolved to: " + value);
|
||||||
|
return "success";
|
||||||
|
}, reason -> {
|
||||||
|
report("First settled promise rejected with: " + reason.toString());
|
||||||
|
return "failure";
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testNativePromises(Object lock) throws InterruptedException {
|
||||||
|
report("Get promise from native method");
|
||||||
|
var nativePromise = getNativePromise(JSString.valueOf("success from native"));
|
||||||
|
nativePromise.then(value -> {
|
||||||
|
report("Native resolved expectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Native rejected unexpectedly with '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
nativePromise = getNativeRejectingPromise(JSString.valueOf("failure from native"));
|
||||||
|
nativePromise.then(value -> {
|
||||||
|
report("Native resolved unexpectedly with '" + value + "'");
|
||||||
|
return value;
|
||||||
|
}, reason -> {
|
||||||
|
report("Native rejected expectedly with '" + reason.toString() + "'");
|
||||||
|
return reason.toString();
|
||||||
|
}).onSettled(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
report("Lock acquired");
|
||||||
|
lock.wait(20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
report("Pass promise to native method");
|
||||||
|
handlePromise(JSPromise.create((resolve, reject) -> {
|
||||||
|
resolve.accept(JSString.valueOf("Resolved from Java"));
|
||||||
|
}));
|
||||||
|
|
||||||
|
handlePromise(JSPromise.create((resolve, reject) -> {
|
||||||
|
reject.accept(JSString.valueOf("Rejected from Java"));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JSBody(params = "msg", script = "return new Promise((resolve, reject) => {"
|
||||||
|
+ " setTimeout(() => resolve(msg), 500);"
|
||||||
|
+ "});")
|
||||||
|
private static native JSPromise<JSString> getNativePromise(JSString msg);
|
||||||
|
|
||||||
|
@JSBody(params = "msg", script = "return new Promise((resolve, reject) => {"
|
||||||
|
+ " setTimeout(() => reject(msg), 500);"
|
||||||
|
+ "});")
|
||||||
|
private static native JSPromise<JSString> getNativeRejectingPromise(JSString msg);
|
||||||
|
|
||||||
|
@JSBody(params = "promise", script = "promise.then("
|
||||||
|
+ " (value) => console.log('success:', value),"
|
||||||
|
+ " (reason) => console.log('failure:', reason));")
|
||||||
|
private static native void handlePromise(JSPromise<JSString> promise);
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
62
samples/promise/src/main/webapp/style.css
Normal file
62
samples/promise/src/main/webapp/style.css
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
html, body {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.block-title {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
height: 20px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.block-content {
|
||||||
|
position: absolute;
|
||||||
|
top: 30px;
|
||||||
|
bottom: 5px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
background-color: rgb(240,240,240);
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: rgb(210,210,210);
|
||||||
|
overflow: auto;
|
||||||
|
padding: 3px;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#description {
|
||||||
|
position: absolute;
|
||||||
|
top: 10px;
|
||||||
|
height: 40px;
|
||||||
|
left: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
#blocks {
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
#stdout-wrapper {
|
||||||
|
top: 0;
|
||||||
|
height: 50%;
|
||||||
|
}
|
||||||
|
#source-wrapper {
|
||||||
|
top: 50%;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
12
samples/promise/src/main/webapp/syntax.css
Normal file
12
samples/promise/src/main/webapp/syntax.css
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.hljs-keyword {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.hljs-class .hljs-title {
|
||||||
|
color: #9BB01D;
|
||||||
|
}
|
||||||
|
.hljs-annotation {
|
||||||
|
color: #FFA500;
|
||||||
|
}
|
||||||
|
.hljs-string, .hljs-number {
|
||||||
|
color: red;
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ include("hello")
|
||||||
include("async")
|
include("async")
|
||||||
include("benchmark")
|
include("benchmark")
|
||||||
include("pi")
|
include("pi")
|
||||||
|
include("promise")
|
||||||
include("kotlin")
|
include("kotlin")
|
||||||
include("scala")
|
include("scala")
|
||||||
include("web-apis")
|
include("web-apis")
|
||||||
|
|
Loading…
Reference in New Issue
Block a user