今天看到一段callback代码
var dataCache = null;
function getAjaxData(callback){
if ( dataCache ) {
callback && callback(dataCache);
} else {
$.ajax({
.....
})
.done( (res) => {
dataCache = res;
callback && callback(res);
})
}
}
上面是一个callback 写的ajax 调用。 设置了一个缓存, 在重复调用函数的时候会取缓存。
这时候调用这个函数会有一个问题。
getAjaxData( () => {
console.log(2);
})
console.log(1);
上面的代码我们会有 先输出1 后输出 2 的 预期。
但实际上在第二次执行该方法会命中缓存,然后先输出 2 后输出 1。 于是导致了BUG。
把callback代码优化为 promise 方式。
var dataCache = null;
function getAjaxData(){
return new Promise( (resolve, rej) => {
if ( dataCache ) {
resolve();
} else {
$.ajax({
.....
})
.done( () => {
resolve();
})
}
})
}
这个时候就不管调用多少次都是异步了 OK, 背景介绍完毕。
那我们进入正题,来看看Promise是如何实现的吧。
var asap = require('asap/raw');
...
...
Promise.prototype.then = function(onFulfilled, onRejected) {
if (this.constructor !== Promise) {
return safeThen(this, onFulfilled, onRejected);
}
var res = new Promise(noop);
handle(this, new Handler(onFulfilled, onRejected, res));
return res;
};
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (Promise._onHandle) {
Promise._onHandle(self);
}
if (self._state === 0) {
if (self._deferredState === 0) {
self._deferredState = 1;
self._deferreds = deferred;
return;
}
if (self._deferredState === 1) {
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
self._deferreds.push(deferred);
return;
}
handleResolved(self, deferred);
}
function handleResolved(self, deferred) {
asap(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
if (self._state === 1) {
resolve(deferred.promise, self._value);
} else {
reject(deferred.promise, self._value);
}
return;
}
var ret = tryCallOne(cb, self._value);
if (ret === IS_ERROR) {
reject(deferred.promise, LAST_ERROR);
} else {
resolve(deferred.promise, ret);
}
});
}
全局搜了 setTimeout 发现没有。 那怎么做到强行异步的呢, 唯一可能的地方就是这里用到的 asap 这个库了。
让我们看看
asap.js
var rawAsap = require("./raw");
var freeTasks = [];
/**
* Calls a task as soon as possible after returning, in its own event, with
* priority over IO events. An exception thrown in a task can be handled by
* `process.on("uncaughtException") or `domain.on("error")`, but will otherwise
* crash the process. If the error is handled, all subsequent tasks will
* resume.
*
* @param {{call}} task A callable object, typically a function that takes no
* arguments.
*/
Calls a task as soon as possible after returning。
we get it!
这个库的实现源码如下, 值得一看
https://github.com/kriskowal/asap/blob/master/browser-raw.js
setTimeout(function(){
console.log(2);
}, 0);
var observer = new MutationObserver(function(){
console.log(1);
});
var node = document.createTextNode("");
observer.observe(node, {characterData: true});
node.data = 1;
console.log(3);
上面代码的输出是什么呢?^ _ ^
欢迎吐槽笔者自己写的 proimse。
https://github.com/KMBaby-zyl/tiny-co/blob/master/promise/promise.js