今天看到一段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