Promise 源码解析—— .then 处理小细节

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

你可能感兴趣的:(Promise 源码解析—— .then 处理小细节)