This is fairly well-covered by the WHAT-WG living standard for HTML's section on async and defer, which includes this handy graphic:

1. What is the meaning of using both async and defer in this context?
If the browser supports async, it ignores defer and does the async work. If not but it supports defer, it does the defer instead. If it doesn't support either, the script blocks DOM parsing, but all modern browsers support at least one.
2.Why did Google choose to use this technique? Does it have any performance or other benefits?
async fetches the script without blocking DOM parsing and rendering, and runs it as soon as it's available even if DOM parsing and rendering is still underway. defer will also avoid blocking DOM parsing and rendering, but won't run the script until parsing is complete (e.g., potentially later).
3. In the onload event, why do they first assign an empty function ( function(){}; ) to the event before calling handleClientLoad()?
This becomes clear if you look at onreadystatechanged: Basically it ensures that handleClientLoad is only called once by GAPI, not potentially twice (once by onload and once by onreadystatechanged.)
4. If I want to move the entire javascript to a separate js file, what's the best approach to load both scripts? Since the new js file will depend on api.js and can't be loaded asynchronously?
Well, it can be loaded asynchronously, you just have to handle the race condition with api.js. I'd probably:
Have handleClientLoad in an inline script above the script tag loading api.js, something like this:
var clientLoaded = false;
function handleClientLoad() {
if (!clientLoaded &&
typeof mainScriptLoad !== "undefined" &&
typeof gapi !== "undefined") {
clientLoaded = true;
mainScriptLoad();
}
}
Have mainScriptLoad in your separate file.
At the end of your separate file, call handleClientLoad.
That way:
- If your script runs first, it'll call
handleClientLoad but handleClientLoad will see that the GAPI isn't loaded yet and won't do anything; later, when the GAPI loads, it will call handleClientLoad and that will call mainScriptLoad because everything is ready.
- If your script runs after GAPI loads, it'll call
handleClientLoad but handleClientLoad will see that your main script isn't loaded yet and not try to call it. Later, when your script loads and calls handleClientLoad, handleClientLoad will call mainScriptLoad because everything is ready.