异步

JS 中的几种异步场景。

  1. setTimeoutsetInterval
  2. 网络请求
  3. 事件绑定

主要是因为 JS 是单线程的。

所以为了实现异步, JS 实现了事件循环机制。

循环过程如下:

  1. 执行script主线程,

  2. 遇到 setTimeoutsetInterval 等宏任务,丢到宏任务队列中去,遇到 PromisenextTick 等微任务,丢到微任务队列中去。

  3. 当前宏任务执行完毕之后,拉取所有微任务队列中的数据,全部执行完毕。

  4. 再取宏任务队列中的第一个任务执行。回到 2。

事件绑定

一个简单的通用的事件绑定函数:

function bindEvent(elem, type, selector, fn) {
  if (fn == null) {
    fn = selector;
    selector = null;
  }
  elem.addEventListener(type, function (e) {
    var target;
    if (selector) {
      target = e.target;
      if (target.matches(selector)) {
        fn.call(target, e);
      }
    } else {
      fn(e);
    }
  });
}

网络请求

一个简单的基于原生 XMLHTTPRequest 实现的网络请求

var xhr = new XMLHttpRequest();
xhr.open("GET", "/api", false);
xhr.onreadystatechange = function () {
  if (xhr.readyState == 4) {
    if (xhr.status === 200) {
      alert(xhr.responseText);
    }
  }
};
xhr.send(null);

readyState 的几种状态:

  • 0 - 未初始化,还没有调用 send()方法
  • 1 - 载入,已调用 send() 方法,正在发送请求
  • 2 - 载入完成,send() 方法执行完成,已经接收到全部响应内容
  • 3 - 交互,正在解析响应内容
  • 4 - 完成,响应内容解析完成,可以在客户端调用了

Promise

// 步骤一:了解promise规范
// 步骤二:实现
// 步骤三:测试
const statusMap = {
  PENDING: "pending",
  FULFILLED: "fulfilled",
  REJECTED: "rejected",
};

// 将promise设置为fulfilled状态
function fulfilledPromise(promise, value) {
  // 只能从pending状态转换为其他状态
  if (promise.status !== statusMap.PENDING) {
    return;
  }
  promise.status = statusMap.FULFILLED;
  promise.value = value;
  runCbs(promise.fulfilledCbs, value);
}
// 将promise设置为rejected状态
function rejectedPromise(promise, reason) {
  // 只能从pending状态转换为其他状态
  if (promise.status !== statusMap.PENDING) {
    return;
  }
  promise.status = statusMap.REJECTED;
  promise.reason = reason;
  runCbs(promise.rejectedCbs, reason);
}
function runCbs(cbs, value) {
  cbs.forEach((cb) => cb(value));
}
function isFunction(fn) {
  return (
    Object.prototype.toString.call(fn).toLocaleLowerCase() ===
    "[object function]"
  );
}
function isObject(obj) {
  return (
    Object.prototype.toString.call(obj).toLocaleLowerCase() ===
    "[object object]"
  );
}
function isPromise(p) {
  return p instanceof Promise;
}

// promise的解析
function resolvePromise(promise, x) {
  // x 与promise相同
  if (promise === x) {
    rejectedPromise(promise, new TypeError("cant be the same"));
    return;
  }
  // x 是promise
  if (isPromise(x)) {
    if (x.status === statusMap.FULFILLED) {
      fulfilledPromise(promise, x.value);
      return;
    }
    if (x.status === statusMap.REJECTED) {
      rejectedPromise(promise, x.reason);
      return;
    }
    if (x.status === statusMap.PENDING) {
      x.then(
        () => {
          fulfilledPromise(promise, x.value);
        },
        () => {
          rejectedPromise(promise, x.reason);
        }
      );
      return;
    }
    return;
  }
  if (isObject(x) || isFunction(x)) {
    let then;
    let called = false;
    try {
      then = x.then;
    } catch (error) {
      rejectedPromise(promise, error);
      return;
    }
    if (isFunction(then)) {
      try {
        then.call(
          x,
          (y) => {
            if (called) {
              return;
            }
            called = true;
            resolvePromise(promise, y);
          },
          (r) => {
            if (called) {
              return;
            }
            called = true;
            rejectedPromise(promise, r);
          }
        );
      } catch (error) {
        if (called) {
          return;
        }
        called = true;
        rejectedPromise(promise, error);
      }
      return;
    } else {
      fulfilledPromise(promise, x);
      return;
    } // x不是对象或者函数
  } else {
    fulfilledPromise(promise, x);
    return;
  }
}
class Promise {
  constructor(fn) {
    this.status = statusMap.PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.fulfilledCbs = []; // then fulfilled callback
    this.rejectedCbs = []; // then rejected callback
    fn(
      (value) => {
        // 防止直接传进来一个thenalbe
        // fulfilledPromise(this, value);
        resolvePromise(this, value);
      },
      (reason) => {
        rejectedPromise(this, reason);
      }
    );
  }

  // 两个参数
  then(onFulfilled, onRejected) {
    const promise1 = this;
    const promise2 = new Promise(() => {});
    if (promise1.status === statusMap.FULFILLED) {
      if (!isFunction(onFulfilled)) {
        return promise1;
      }
      setTimeout(() => {
        try {
          const x = onFulfilled(promise1.value);
          resolvePromise(promise2, x);
        } catch (error) {
          rejectedPromise(promise2, error);
        }
      }, 0);
    }
    if (promise1.status === statusMap.REJECTED) {
      if (!isFunction(onRejected)) {
        return promise1;
      }
      setTimeout(() => {
        try {
          const x = onRejected(promise1.reason);
          resolvePromise(promise2, x);
        } catch (error) {
          rejectedPromise(promise2, error);
        }
      }, 0);
    }
    if (promise1.status === statusMap.PENDING) {
      onFulfilled = isFunction(onFulfilled)
        ? onFulfilled
        : (value) => {
            return value;
          };
      onRejected = isFunction(onRejected)
        ? onRejected
        : (err) => {
            throw err;
          };
      promise1.fulfilledCbs.push(() => {
        setTimeout(() => {
          try {
            const x = onFulfilled(promise1.value);
            resolvePromise(promise2, x);
          } catch (error) {
            rejectedPromise(promise2, error);
          }
        }, 0);
      });
      promise1.rejectedCbs.push(() => {
        setTimeout(() => {
          try {
            const x = onRejected(promise1.reason);
            resolvePromise(promise2, x);
          } catch (error) {
            rejectedPromise(promise2, error);
          }
        }, 0);
      });
    }
    return promise2;
  }
}

// 测试用到的钩子
Promise.deferred = function () {
  const deferred = {};
  deferred.promise = new Promise((resolve, reject) => {
    deferred.resolve = resolve;
    deferred.reject = reject;
  });
  return deferred;
};

module.exports = Promise;

Generator

一个可迭代的对象的例子:

function createIterator(items) {
  var i = 0;
  return {
    next: function () {
      var done = i >= items.length;
      var value = !done ? items[i++] : undefined;
      return {
        done: done,
        value: value,
      };
    },
  };
}

var iterator = createIterator([1, 2, 3]);

iterator.next();
iterator.next();
iterator.next();
iterator.next();

使用 yield 的情况, yield 会替换前一个的返回值。

// yield 例子
function* createIterator() {
  let first = yield 1;
  let second = yield first + 2;
  yield second + 3;
}
let iterator = createIterator();

iterator.next();
iterator.next(4);
iterator.next(5);
iterator.next();

Thunk 函数自动执行 generator:

// 代码
function run(fn) {
  var gen = fn(); //获得生成器对象

  // 递归调用自身的方法(类似尾递归的思路,执行完之后调用自身)
  function next(err, data) {
    // 获取生成器next执行之后的返回值。
    var result = gen.next(data);
    // 如果done了,就返回
    if (result.done) return;
    // 如果没有done,就调用自身,给fs.readFile的回调,让它在执行完之后
    // 执行next,next的传参是callback的传参
    result.value(next);
  }

  // 先执行一次
  next();
}

const Thunk = function (fn) {
  return function (...args) {
    return function (callback) {
      return fn.call(this, ...args, callback);
    };
  };
};

// 使用
const fs = require("fs");

const readFileThunk = Thunk(fs.readFile);

const g = function* () {
  const s1 = yield readFileThunk("xxx.xx");
  console.log(s1);
  const s2 = yield readFileThunk("xxx.xx");
  console.log(s2);
};

async/await

async/await 其实就是 generator 的语法糖, await 会被转译成 yield ,然后通过 generator 自执行来完成异步。

其实就是使用了 Promise.then 来完成上面的 callback 完成的东西,保证了返回值是 Promise

async function example(params) {
  // xxx
}

// =>

function example(params) {
  return spawn(function* () {
    // xxx
  });
}

function spawn(genF) {
  return new Promise(function (resolve, reject) {
    const gen = genF(); // 生成器对象
    function step(nextF) {
      let next;
      try {
        next = nextF(); // 执行gen.next
      } catch (e) {
        return reject(e);
      }
      if (next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(
        function (v) {
          step(function () {
            return gen.next(v);
          });
        },
        function (e) {
          step(function () {
            return gen.throw(e);
          });
        }
      );
    }
    step(function () {
      return gen.next(undefined);
    });
  });
}