对nodejs无限循环函数执行的奇怪观察

时间:2018-08-14 15:28:18

标签: node.js async-await infinite-loop

我最近遇到了文章Tail call optimization in ECMAScript 6。我对测试TCO行为很感兴趣(尽管后来我发现文章提到的nodejs 8+不支持TCO),并且发现了我无法理解的行为。

  1. 普通循环功能

    'use strict';
    
    process.on('SIGTERM', () =>
    {
        console.log('SIGTERM received');
        process.exit(0);
    })
    
    process.on('SIGINT', () =>
    {
        console.log('SIGINT received');
        process.exit(0);
    })
    
    process.on('uncaughtException', (error) =>
    {
        console.error('Uncaught exception', error);
        process.exit(1);
    })
    
    process.on('unhandledRejection', (error) =>
    {
        console.error('Unhandled rejection', error);
        process.exit(0);
    })
    
    let counter = 0;
    
    function test()
    {
        console.log(`Counter: ${counter++}`);
        test();
    }
    
    console.log('Test started');
    
    test();
    
    console.log('Test ended');
    

    此版本的代码产生:

    Test started
    Counter: 0
    ...
    Counter: 10452
    Uncaught exception RangeError: Maximum call stack size exceeded
        at WriteStream.removeListener (events.js:306:28)
        at write (console.js:130:12)
        at Console.log (console.js:135:3)
        at test (/test.js:31:13)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
        at test (/test.js:32:5)
    
  2. 异步循环功能,功能内无任何等待

    'use strict';
    
    process.on('SIGTERM', () =>
    {
        console.log('SIGTERM received');
        process.exit(0);
    })
    
    process.on('SIGINT', () =>
    {
        console.log('SIGINT received');
        process.exit(0);
    })
    
    process.on('uncaughtException', (error) =>
    {
        console.error('Uncaught exception', error);
        process.exit(1);
    })
    
    process.on('unhandledRejection', (error) =>
    {
        console.error('Unhandled rejection', error);
        process.exit(0);
    })
    
    let counter = 0;
    
    async function test()
    {
        console.log(`Counter: ${counter++}`);
        test();
    }
    
    console.log('Test started');
    
    test();
    
    console.log('Test ended'); 
    

    此版本的代码产生:

    Test started
    Counter: 0
    ...
    Counter: 6967
    Test ended
    
  3. 异步循环功能,该功能在功能内处于等待状态

    'use strict';
    
    const bluebird = require('bluebird');
    
    process.on('SIGTERM', () =>
    {
        console.log('SIGTERM received');
        process.exit(0);
    })
    
    process.on('SIGINT', () =>
    {
        console.log('SIGINT received');
        process.exit(0);
    })
    
    process.on('uncaughtException', (error) =>
    {
        console.error('Uncaught exception', error);
        process.exit(1);
    })
    
    process.on('unhandledRejection', (error) =>
    {
        console.error('Unhandled rejection', error);
        process.exit(0);
    })
    
    let counter = 0;
    
    async function test()
    {
        await bluebird.delay(1);
        console.log(`Counter: ${counter++}`);
        test();
    }
    
    console.log('Test started');
    
    test();
    
    console.log('Test ended');
    

    此版本的代码不间断运行。

摘要:

  1. 普通循环功能:在计数器10452处停止并抛出RangeError:超出了最大调用堆栈大小
  2. 该函数内没有任何等待的异步循环功能:在计数器6967处停止,没有任何错误
  3. 函数中具有await的异步循环功能:不间断运行

有人能解释这种行为差异或将我指向可以谷歌搜索的任何关键字吗?

0 个答案:

没有答案