Chrome: Invoking 'alert()' during

2019-02-25 07:37发布

问题:

While working on my web app, specifically file uploads, Chrome displayed a warning after I called an alert:

Invoking 'alert()' during microtask execution is deprecated and will be removed in M53, around September 2016. See https://www.chromestatus.com/features/5647113010544640 for more details.

However I think that in my situation the call is justified and am a little worried my code won't work once M53 is released. Note that I'm not shipping to production with the alerts, but for testing it is quite valuable.

The situation:

I am developing my app in typescript using react. I'm using axios to do http requests. Basically the http-post looks like this:

axios.post("/upload/", data)
    .then((response: any) => {
        callback(undefined);
    })
    .catch((error: any) => {
        callback(error);
    });

Then in the calling method I'm popping an alert if there's an error so I can be sure the tester/developer will be notified. Kinda like this:

this.service.uploadFile((error: any) => {
    if (error) {
        console.log(error);
        alert("An error occured");
        return;
    }

    this.onUploadCompleted()
});

This is when chrome displays the warning.

First of all I'm wondering if the warning is justified, because the alert is shown until after the request is done and an error was returned. So I'm pretty sure it's not blocking anything.

If it is justified, what can be done so I can display the alert anyway?

回答1:

Yes, the warning is justified: You're calling alert within a microtask, in this case a promise completion. (See Difference between microtask and macrotask within an event loop context.)

alert, prompt, and confirm are relics of an age long past, and they have a problem: They completely interrupt the normal workings of JavaScript and arguably violate its run-to-completion semantics by completely suspending execution, right in the middle of a job from the job queue (or a task from the event loop; JavaScript and the HTML5 spec differ on terminology) and do UI in the form of a blocking modal. This is just not in keeping with the general mode of interaction, which is event-based (show the message, get an event when it's closed).

You can work around it by doing the alert in a macrotask instead:

this.service.uploadFile((error: any) => {
    if (error) {
        setTimeout(() => {
            console.log(error);
            alert("An error occured");
        }, 0);
        return;
    }

    this.onUploadCompleted()
});

...but really the solution is to stop using alert, prompt, and confirm entirely.


Here's a fun example of micro and macro tasks: A promise completion is a microtask, whereas timer callbacks are macrotasks. The initial run of the script is also a macrotask, as is a DOM event callback. All of the microtasks queued by a macrotask are run before the next macrotask is run; e.g., they jump the queue. So with this:

// First, we set a timer callback for 0ms
setTimeout(() => {
  console.log("timer fired");
}, 0);

// Now we get a promise that's *already* resolved and hook a callback
Promise.resolve().then(() => {
  console.log("promise resolved");
});

// Now show a message demonstrating we got here before the promise resolution callback
console.log("got to the end");

...we see

got to the end
promise resolved
timer fired

...rather than

got to the end
timer fired
promise resolved

....which we would have gotten if all tasks were created equal.