Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

microtask and macrotask 相关思考 #5

Open
maiff opened this issue Feb 25, 2017 · 0 comments
Open

microtask and macrotask 相关思考 #5

maiff opened this issue Feb 25, 2017 · 0 comments
Labels

Comments

@maiff
Copy link
Owner

maiff commented Feb 25, 2017

microtask and macrotask 相关思考

标签(空格分隔): JS


先看一段代码:

setImmediate(function(){
    console.log(1);
},0);
setTimeout(function(){
    console.log(2);
},0);
new Promise(function(resolve){
    console.log(3);
    resolve();
    console.log(4);
}).then(function(){
    console.log(5);
});
console.log(6);
process.nextTick(function(){
    console.log(7);
});
console.log(8);

正确顺序: 3 4 6 8 7 5 2 1
为什么了,原来我以前的理解js里只有一个队列,但是经过最近的搜索发现不是,js里有两种task queue,一个叫microtask一个是macrotask。
分别有什么了?

macrotasks: script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
microtasks: process.nextTick, Promises, Object.observe, MutationObserver

PS.MutationObserver 的解释
那执行顺序是什么了?

One go-around of the event loop will have exactly one task being processed from the macrotask queue (this queue is simply called the task queue in the WHATWG specification). After this macrotask has finished, all available microtasks will be processed, namely within the same go-around cycle. While these microtasks are processed, they can queue even more microtasks, which will all be run one by one, until the microtask queue is exhausted.

简单来说,就是JavaScript引擎首先从macrotask queue中取出第一个任务,
执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;
然后再从macrotask queue中取下一个,
执行完毕后,再次将microtask queue中的全部取出;
循环往复,直到两个queue中的任务都取完。

那上面代码理解了吧,至于process.nextTick在promise前面,正如他字面的意思就是,在主任务队列运行玩就立马运行,详细见这篇官方文章
这篇文章解答了上面大部分问题:

  • 为什么setImmediate在setTimeout后面:
  • setImmediate() is designed to execute a script once the current poll phase completes.
  • setTimeout() schedules a script to be run after a minimum threshold in ms has elapsed.

The order in which the timers are executed will vary depending on the context in which they are called. If both are called from within the main module, then timing will be bound by the performance of the process (which can be impacted by other applications running on the machine).

// timeout_vs_immediate.js
setTimeout(function timeout () {
  console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});
$ node timeout_vs_immediate.js
timeout
immediate

$ node timeout_vs_immediate.js
immediate
timeout
// timeout_vs_immediate.js
var fs = require('fs')

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('timeout')
  }, 0)
  setImmediate(() => {
    console.log('immediate')
  })
})

$ node timeout_vs_immediate.js
immediate
timeout

$ node timeout_vs_immediate.js
immediate
timeout

简而言之在没有IO包括的情况下,运行是不确定的但是在IO圈下是setImmediate立即执行,但是加了上面的示例代码中却setImmediate永远是最后。(等会研究一下)

  • process.nextTick() 和 setImmediate

We recommend developers use setImmediate() in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)
在setImmediate和process.nextTick()都可以的情况下优先用setImmediate因为可以兼容大部分环境。

然后有以下几种情况用process.nextTick()

  • Allow users to handle errors, cleanup any then unneeded resources, or perhaps try the request again before the event loop continues.

  • At times it's necessary to allow a callback to run after the call stack has unwound but before the event loop continues.

打个比方

const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
  EventEmitter.call(this);
  this.emit('event');
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
  console.log('an event occurred!');
});

这个事件永远不会运行,因为触发事件在new的时候就触发了,所以要这样:

const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
  EventEmitter.call(this);

  // use nextTick to emit the event once a handler is assigned
  process.nextTick(function () {
    this.emit('event');
  }.bind(this));
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
  console.log('an event occurred!');
});

在http模块就是这样:

var server = net.createServer();
server.on('connection', function(conn) { });

server.listen(8080);
server.on('listening', function() { });

最后

// this has an asynchronous signature, but calls callback synchronously
function someAsyncApiCall (callback) { callback(); };

// the callback is called before `someAsyncApiCall` completes.
someAsyncApiCall(() => {

  // since someAsyncApiCall has completed, bar hasn't been assigned any value
  console.log('bar', bar); // undefined

});

var bar = 1;
function someAsyncApiCall (callback) {
  process.nextTick(callback);
};

someAsyncApiCall(() => {
  console.log('bar', bar); // 1
});

var bar = 1;

设计回调的时候要想清楚~

参考链接:

@maiff maiff added the blog label Feb 25, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant