# eventual send with E()
In web browsers, a common pattern of remote communication is using the asynchronous fetch API with promises (opens new window):
const init = fetch('products.json')
.then(response => response.json())
.then(products => initialize(products))
.catch(err => {
console.log(`Fetch problem: ${err.message}`);
});
In the Agoric platform, communicating with remote objects is similar,
using the E()
wrapper. For example,
a deploy script may want to use the
Zoe Service API to install a contract on a blockchain.
But the deploy script cannot call zoe.install(bundle)
, because it does not have local
access to the zoe
object. However, the deploy
script is given a zoe
remote presence. To call methods on the
actual Zoe object, the deploy script can do:
import { E } from '@endo/eventual-send';
E(zoe).install(bundle)
.then(installationHandle => { ... })
.catch(err => { ... });
# Eventual Send
One of the ways Zoe partitions risk (opens new window) is by running in its own vat, separate from any smart contract that might use too much compute time or heap space. The smart contracts also run in separate vats.
What happens when we call E(zoe).install(bundle)
is an eventual send:
- A message consisting of a the method name
install
with the arguments structure[bundle]
is marshaled to a flat string and queued for delivery to the vat thatzoe
comes from. E(zoe).install(bundle)
returns a promise for the result.- The
then
andcatch
methods queue callbacks for when the promise is resolved or rejected. Execution continues until the stack is empty and thus this turn through the event loop completes. - Eventually
zoe
responds, which results in a new message in this vat's message queue and a new turn through the event loop. The message is de-serialized and the results are passed to the relevant callback.
This way, you can communicate with objects in separate vats as easily as objects in the same vat with one wrinkle: the communication must be asynchronous.
The E()
wrapper works with:
- remote presences (local proxies for objects in remote vats)
- local objects (in the same vat)
- promises for remote presences or local objects
In all cases, E(x).method(...args)
returns a promise.
Promise Pipelining
Since E()
accepts promises, we can compose eventual sends:
E(E(object1).method1(...args1)).method2(...args2)
. This way
we can take advantage of promise pipelining so that a single
round trip suffices for both method calls.
Troubleshooting remote calls
The E()
function creates an
forwarder that doesn't know what methods the remote object has.
If you misspell or incorrectly capitalize the method name,
the local environment can't tell you've done so. You'll only find out at runtime when the
remote object complains that it doesn't know that method.
If an ordinary synchronous call (obj.method()
) fails because the method doesn't exist,
consider that obj
may be remote and try E(obj).method()
.
Watch: How Agoric Solves Reentrancy Hazards (Nov 2020)
for more on eventual send and remote communication