author: 陈家宾
email: 617822642@qq.com
date: 2018/2/23
Promise 基本原理
var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;
function Promise(fn) {
var state = PENDING;
var value = null;
var handlers = [];
doResolve(fn, resolve, reject)
function fulfill(result) {}
// state=fulfilled, value=result
function reject(error) {}
// state=rejected, value=error
function resolve(result) {}
// if then in result(result 是个 promise), 执行 doResolve
// else 执行 fulfill
function doResolve(fn, onFulfilled, onRejected) {}
// 给 fn 传入 resolve 和 reject 函数
// resolve 函数,执行 onFulfilled
// reject 函数,执行 onRejected
function handle(handler) {}
// if PENDING, push handler
// if FULFILLED, 执行 handler 中的 onFulfilled 函数
// if REJECTED, 执行 handler 中的 onRejected 函数
this.done = function (onFulfilled, onRejected) {}
// 异步(setTimeout 0)执行 handler({onFulfilled, onRejected})
this.then = function (onFulfilled, onRejected) {
var self = this
return new Promise((resolve,reject)=>{
return self.done(result=>{
if onFulfilled
return resolve(onFulfilled(result))
else
return resolve(result)
}, err=>{
if onRejected
return resolve(onRejected(err))
else
return reject(err)
})
})
}
}
基本语法
new Promsie((resolve, reject) => {
// function1()
resolve()
}).then(() => {
// function2()
})
这里 function1 同步执行,function2 异步执行
Promise 本轮循环 & 次轮循环
本来以为 Promise 的内容就到此为止了,后来看到了阮老师的一篇博客,里面说异步分两种,what?!!
异步任务可以分成两种。
- 追加在本轮循环的异步任务
- 追加在次轮循环的异步任务
Node 规定,
process.nextTick
和Promise
的回调函数,追加在本轮循环,即同步任务一旦执行完成,就开始执行它们。而setTimeout
、setInterval
、setImmediate
的回调函数,追加在次轮循环。
异步分两种已经让我大开眼界,但在我的知识世界里,Promise 不是用 setTimeout 来实现异步的吗,为什么 Promise 和 setTimeout 还分属于不同的异步类型里呢?
OK,马上找一下 Promise 的 polyfill 原码
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) { // BrowserMutationObserver = window.MutationObserver
scheduleFlush = useMutationObserver();
} else if (isWorker) { // typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') { // browserWindow = typeof window !== 'undefined' ? window : undefined
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}
原来 Promise 的异步不仅仅只是 setTimeout,这里会根据不同环境来采用不同的实现方式,浏览器中主要用了 MutationObserver 和 setTimeout
我们先来看一下 MutationObserver 的兼容性(下图参考 https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
由上图可知,使用 polyfill,从以上版本开始,Promise 是由 MutationObserver 实现的本轮循环的异步任务,低于以上版本的,则是由 setTimeout 实现的次轮循环的异步任务(本轮循环在次轮循环之前执行)。其带来的具体差别如下:
// ie11
setTimeout(function () {console.log(1)});
Promise.resolve().then(function () {
console.log(2);
});
// 输出结果为 2 1
// ie10
setTimeout(function () {console.log(1)});
Promise.resolve().then(function () {
console.log(2);
});
// 输出结果为 1 2
其他
shim VS polyfill
- shim,是一种能给旧环境带来新 API 的库
- polyfill 则是针对浏览器的一种 shim,一种补充
单词 polyfill 的由来:
Polyfilla is a UK product known as Spackling Paste in the US. With that in mind: think of the browsers as a wall with cracks in it. These [polyfills] help smooth out the cracks and give us a nice smooth wall of browsers to work with.——Remy Sharp
参考资料
- 《Node 定时器详解》,阮一峰,2018年2月23日,http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html
- What is the difference between a shim and a polyfill? ,stack overflow,closed at Jul 30 ‘15,https://stackoverflow.com/questions/6599815/what-is-the-difference-between-a-shim-and-a-polyfill
- 《Speaking JavaScript》,Axel Rauschmayer,March 2014,http://speakingjs.com/es5/ch30.html#id1267739