const args = process.argv.slice(2) if (args.length == 0) { console.error("Error: missing event loop arg"); process.exit(1) } const which = args[0] /* * Call stack pushes functions onto the stack and executes * the one on the end each event loop * So in this example the stack goes like this * foo1 * foo1 console.log * foo1 * foo1 bar1 * foo1 * foo1 baz1 * foo1 * - empty */ if (which == 1) { const bar1 = () => console.log('stack: bar1') const baz1 = () => console.log('stack: baz1') const foo1 = () => { console.log('stack: foo1') // 2 bar1() // 3 baz1() // 4 } foo1() // 1 } /* * setTimeout timers are run in an IO thread and their callbacks * are put into the Message Queue which only is processed after * the timer or IO is complet and the Call Stack is empty * foo2 * foo2 console.log * foo2 * foo2 setTimeout * foo2 * foo2 baz2 * foo2 * bar2 * bar2console.log * bar2 */ if (which == 2) { const bar2 = () => console.log('setTimeout: bar2') const baz2 = () => console.log('stack: baz2') const foo2 = () => { console.log('stack: foo2') // 2 setTimeout(bar2, 0) // 4 baz2() // 3 } foo2() // 1 } /* * ES6 introduced the Job Queue allows us to process async * results as soon as possible * CS foo3 * CS foo3 console.log * CS foo3 * CS foo3 setTimeout * CS foo3 new Promise * CS foo3 new Promise console.log * CS foo3 new Promise resolve * CS foo3 new Promise * CS foo3 * CS foo3 baz3 * CS foo3 * JQ resolve => console.log * MQ bar3 */ if (which == 3) { const bar3 = () => console.log('setTimeout: bar3') const baz3 = () => console.log('stack: baz3') const foo3 = () => { console.log('stack: foo3') // 2 setTimeout(bar3, 0) // 8 new Promise((resolve) => { // 3 console.log("stack: new Promise setup") // 4 resolve('resolve from promise: should be right after baz, before bar') // 5 }).then(resolve => console.log(`then: ${resolve}`)) // 7 baz3() // 6 } foo3() // 1 } /* * process.nextTick will run a function at the end of the current * tick and before the next one, and before the queues are processed * process.nextTick // callback * foo4 * foo4 console.log * foo4 * foo4 setTimeout * foo4 * foo4 new Promise * foo4 new Promise resolve * foo4 * foo4 baz4 * foo4 * console.log("next tick") // nexTick * resolve => console.log // Promise resolve callback * bar4 // setTimeout */ if (which == 4) { process.nextTick(() => { // 1 console.log("process.nextTick") // 7 }) const bar4 = () => console.log('setTimout: bar4') const baz4 = () => console.log('stack: baz4') const foo4 = () => { console.log("stack: foo4") // 3 setTimeout(bar4, 0) // 9 new Promise((resolve, reject) => { // 4 console.log("stack: new Promise") resolve('res4') // 5 }).then(resolve => console.log(`then: ${resolve}`)) // 8 baz4() // 6 } foo4() // 2 } /* * setImmediate is like setTimeout 0 */ if (which == 5) { process.nextTick(() => { console.log("process.nextTick") }) // this runs in the next tick and is essentially the // same as setTimeout with 0 // running this several times will show them appearing // back and forth in a race condition setImmediate(() => { console.log("setImmediate") }) const bar5 = () => console.log('setTimeout: bar5') const baz5 = () => console.log('stack: baz5') const foo5 = () => { console.log("stack: foo5") setTimeout(bar5, 0) new Promise((resolve, reject) => { console.log("stack: new Promise") resolve('res5') }).then(resolve => console.log(`then: ${resolve}`)) // 8 baz5() } foo5() }