The await
keyword blocks continuation of execution until a promise has been resolved (or rejected, which throws an Error).
This is nothing specific to waffle, it's a feature of modern JavaScript and was introduced to solve the problem of the "callback hell".
Before await
and Promises things looked like this:
contract.function(arg, function callback(err, result) {
if (err) return cb(err);
expect(result).to.be.equal.to(something);
cb();
});
Then, with promises and ES6, things changed to this:
contract.function(arg).catch((err) => {
cb(err);
}).then((result) => {
expect(result).to.be.equal.to(something);
cb();
});
Now with async/await we can get rid of all callbacks:
await expect(await contract.function(arg)).to.be.equal.to(something);
Basically await blocks and wait until the promise returns an result or error. If it's an error, the error is thrown. If it's a result, it's returned like a return value.
This immensely improves readability once you can wrap your head around the concept. There are plenty of resources online with further explanations - as said, look for general JavaScript explanations, since this is not specific to waffle/web3.
I noticed I missed the actual question: Why does .to.equal
need await and .to.be.reverted
doesn't.
expect(await contract.function()).to.equal(something);
Here you are first awaiting for contract.function()
to return a result. Then you want expect to compare that result with something.
await expect(contract.function()).to.be.reverted;
Here you are not checking the result of contract.function()
but instead you are having expect check whether the Promise returned ended up being rejected.
Another approach to the answer:
p = contract.function()
always returns a Promise, here p
. A Promise is basically an object that you can tell that you want to be informed when the result is available. The result of a Promise can either be, that it was Resolved (transaction was successful, maybe with data) or that it was Rejected (transaction failed, maybe with error).
v = await p
always takes a Promise, here p
. Await takes that promise and makes sure that execution in the current context doesn't continue on until the Promise yielded a result. If the Promise resolves, await returns the data (value v
) of the successful transaction. If the Promise was rejected, an Error is thrown with the err data.
expect(v).to.be.equal.to(something);
- when comparing two values, you need actual values - not the Promise that there might be a value later. So if the value comes from a Promise, you must first await
it.
expect(p).to.be.reverted;
- when checking whether a transaction reverted (= Promise rejected) - we can't await
for the value v
, because we expect there won't be a value, but instead an error. So instead of passing a value we won't get, we instead pass the Promise that is supposed to be rejected.