In JavaScript, does using await inside a loop block the loop?

前端 未结 8 2420
南旧
南旧 2020-12-02 06:38

Take the following loop:

for(var i=0; i<100; ++i){
    let result = await some_slow_async_function();
    do_something_with_result();
}
相关标签:
8条回答
  • 2020-12-02 06:49

    You can test async/await inside a "FOR LOOP" like this:

    (async  () => {
            for (let i = 0; i < 100; i++) {
                    await delay();
                    console.log(i);
            }
    })();
    
    function delay() {
            return new Promise((resolve, reject) => {
                    setTimeout(resolve, 100);
            });
    }

    0 讨论(0)
  • 2020-12-02 06:53

    No Event loop isn't blocked, see example below

    function sayHelloAfterSomeTime (ms) {
      return new Promise((resolve, reject) => {
        if (typeof ms !== 'number') return reject('ms must be a number')
        setTimeout(() => { 
          console.log('Hello after '+ ms / 1000 + ' second(s)')
          resolve()  
        }, ms)
      })
    }
    
    async function awaitGo (ms) {
       await sayHelloAfterSomeTime(ms).catch(e => console.log(e))
       console.log('after awaiting for saying Hello, i can do another things  ...')
    }
    
    function notAwaitGo (ms) {
    	sayHelloAfterSomeTime(ms).catch(e => console.log(e))
        console.log('i dont wait for saying Hello ...')
    }
    
    awaitGo(1000)
    notAwaitGo(1000)
    console.log('coucou i am event loop and i am not blocked ...')

    0 讨论(0)
  • 2020-12-02 06:57

    async functions return a Promise, which is an object that will eventually "resolve" to a value, or "reject" with an error. The await keyword means to wait until this value (or error) has been finalized.

    So from the perspective of the running function, it blocks waiting for the result of the slow async function. The javascript engine, on the other hand, sees that this function is blocked waiting for the result, so it will go check the event loop (ie. new mouse clicks, or connection requests, etc.) to see if there are any other things it can work on until the results are returned.

    Note however, that if the slow async function is slow because it is computing lots of stuff in your javascript code, the javascript engine won't have lots of resources to do other stuff (and by doing other stuff would likely make the slow async function even slower). Where the benefit of async functions really shine is for I/O intensive operations like querying a database or transmitting a large file where the javascript engine is well and truly waiting on something else (ie. database, filesystem, etc.).

    The following two bits of code are functionally equivalent:

    let result = await some_slow_async_function();
    

    and

    let promise = some_slow_async_function(); // start the slow async function
    // you could do other stuff here while the slow async function is running
    let result = await promise; // wait for the final value from the slow async function
    

    In the second example above the slow async function is called without the await keyword, so it will start execution of the function and return a promise. Then you can do other things (if you have other things to do). Then the await keyword is used to block until the promise actually "resolves". So from the perspective of the for loop it will run synchronous.

    So:

    1. yes, the await keyword has the effect of blocking the running function until the async function either "resolves" with a value or "rejects" with an error, but it does not block the javascript engine, which can still do other things if it has other things to do while awaiting

    2. yes, the execution of the loop will be sequential

    There is an awesome tutorial about all this at http://javascript.info/async.

    0 讨论(0)
  • 2020-12-02 06:58

    As @realbart says, it does block the loop, which then will make the calls sequential.

    If you want to trigger a ton of awaitable operations and then handle them all together, you could do something like this:

    const promisesToAwait = [];
    for (let i = 0; i < 100; i++) {
      promisesToAwait.push(fetchDataForId(i));
    }
    const responses = await Promise.all(promisesToAwait);
    
    0 讨论(0)
  • 2020-12-02 07:08
    1. Does await block the loop? Or does the i continue to be incremented while awaiting?

    "Block" is not the right word, but yes, i does not continue to be incremented while awaiting. Instead the execution jumps back to where the async function was called, providing a promise as return value, continuing the rest of the code that follows after the function call, until the code stack has been emptied. Then when the awaiting is over, the state of the function is restored, and execution continues within that function. Whenever that function returns (completes), the corresponding promise -- that was returned earlier on -- is resolved.

    1. Is the order of do_something_with_result() guaranteed sequential with regard to i? Or does it depend on how fast the awaited function is for each i?

    The order is guaranteed. The code following the await is also guaranteed to execute only after the call stack has been emptied, i.e. at least on or after the next microtask can execute.

    See how the output is in this snippet. Note especially where it says "after calling test":

    async function test() {
        for (let i = 0; i < 2; i++) {
            console.log('Before await for ', i);
            let result = await Promise.resolve(i);
            console.log('After await. Value is ', result);
        }
    }
    
    test().then(_ => console.log('After test() resolved'));
    
    console.log('After calling test');

    0 讨论(0)
  • 2020-12-02 07:12

    Here is my test solution about this interesting question:

    import crypto from "crypto";
    
    function diyCrypto() {
        return new Promise((resolve, reject) => {
            crypto.pbkdf2('secret', 'salt', 2000000, 64, 'sha512', (err, res) => {
                if (err) {
                    reject(err)
                    return 
                }
                resolve(res.toString("base64"))
            })
        })
    }
    
    setTimeout(async () => {
        console.log("before await...")
        const a = await diyCrypto();
        console.log("after await...", a)
    }, 0);
    
    setInterval(() => {
        console.log("test....")
    }, 200);
    

    Inside the setTimeout's callback the await blocks the execution. But the setInterval is keep runnning, so the Event Loop is running as usual.

    0 讨论(0)
提交回复
热议问题