mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-21 01:00:54 +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("benchmark")
|
||||
include("pi")
|
||||
include("promise")
|
||||
include("kotlin")
|
||||
include("scala")
|
||||
include("web-apis")
|
||||
|
Loading…
Reference in New Issue
Block a user