简体   繁体   中英

How to call the callback function within selenium execute_async_script from python

i am scraping one website, there is audio playing from the webpage, i want to know when will the audio finish playing, so i add event listener to the audio webElement to monitor 'onEnded' event to fire, and i want to get noticed from the callback() function within driver.execute_async_script.

but i always get error

TypeError: <function MissionContentItemHelper.audio_play_end at 0x10f3b7b70> is not JSON serializable.

my questions are:

1.does arguments[arguments.length - 1] refer to self.audio_play_end?

2.why the error output is about 'MissionContentItemHelper.audio_play_end is not JSON serializable'? i can't see any clue between MissionContentItemHelper.audio_play_end and JSON.

3.is the way calling audio_play_end correct?

class MissionContentItemHelper: 
    def sleep(self):
        audio = self.browser.find_element_by_css_selector('#audio')
        self.browser.execute_async_script("""
            var audio = arguments[0];
            callback = arguments[arguments.length - 1];
            audio.addEventListener("ended", function(_event) {
                callback();
            }
        """, audio, self.audio_play_end)

    @staticmethod
    def audio_play_end():
        print('the audio finished playing...')

execute_async_script 's name confuses many people. It doesn't executes the script asynchronously from your test code, it only allows you to execute a script which runs asynchronously from the browser's main thread, like AJAX calls and JavaScript setTimeout .

These JavaScript functions and alike themselves return almost immediately, but they take a (JavaScript) function argument that is executed when the asynchronous operation completes. What execute_async_script does is exactly like execute_script , but instead of returning when the function itself returns (which is almost immediately) it provides its own callback JavaScript function (which it passes as the last argument, that's why the arguments[arguments.length - 1] is needed), that you can use in the JavaScript code you're passing into execute_async_script , to pass it on to the asynchronous function as the callback. execute_async_script returns only when this callback is called, instead of returning immediately.

For example, in the following code, the call to execute_async_script acts like a delay of 2 seconds:

browser.execute_async_script("setTimeout(arguments[0], 2000)")

Note that because I didn't pass any additional arguments to the script, then arguments[0] is actually the last argument.

In your case, you don't have to pass self.audio_play_end , but you have to call audio.play() inside your JavaScript snippet to start the audio. execute_async_script should return only when the audio is finished playing.

Usage works as following:

# driver allready i initialised

js = """
     var done = arguments[0];
     // my_async_acript here ..
        .then(value=>done(value)
"""
value = driver.execute_async_script(js)

Call javascript function done(value) with the value you want to return .

Note: If you're getting a timeout-exception, check the developer-console for errors

This actually is a dublicate I think reference

It's important to know that the last argument passed is the promise resolver. That means:

self.browser.execute_async_script("""
  let [element, resolve] = arguments
  element.addEventListener("ended", resolve)    
""", audio)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM