Skip to content

Latest commit

 

History

History
389 lines (316 loc) · 9.37 KB

Promise.md

File metadata and controls

389 lines (316 loc) · 9.37 KB

Promise

目录

入门

优势

避免回调地狱

callback:

callback hell

promise:

promise

转移控制权

  • callback 的控制权交给了调用方
  • promise 的控制权属于使用者

特性

  1. new Promise( ... ) 返回一个 Promise 实例,可以被链式调用,即 thenable
  2. 每次调用 then 函数时都会默认返回一个 Promise 实例,即可以继续链式调用。
  3. then 函数内部可以显示 return 一个新的 Promise 实例,或者其他值。
    • then 函数内部显示返回其他值时,会立即执行下一级的 then 函数。
    • then 函数内部显示返回一个新的 Promise 实例时,下一级的 then 函数会在新的 Promise 实例状态确认后被调用。

示例

throw new Error()

new Promise((resolve, reject) => {
  setTimeout(() => {
    throw new Error('miss error');
  }, 2000);
})
  .then(value => {
    console.log(value);
  })
  .catch(error => {
    console.log('mmm:', error);
  });

因为异步函数的回调是在事件队列里单独拉出来执行的。所以在异步函数外面包裹 try-catch 是无法捕捉到回调函数里抛出的异常的。因为当回调函数从队列里被拉出来执行的时候 try-catch 所在的代码块已经执行完毕了。

new Promise((resolve, reject) => {
  throw new Error('miss error');
})
  .then(value => {
    console.log(value);
  })
  .catch(error => {
    console.log('mmm:', error);
  });

此时 Promise 的 catch 函数可以捕获异常。

  1. 正确做法
new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('miss error'));
  }, 2000);
})
  .then(value => {
    console.log(value);
  })
  .catch(error => {
    console.log('mmm:', error);
  });

最佳实践

避免 then 函数里面嵌套 then 函数

// bad
Promise.resolve('hello world')
  .then(value => {
    console.log('outer: step 1');

    return Promise.resolve(value)
      .then(value => {
        console.log('inner: step 1');
        return value;
      })
      .then(value => {
        console.log('inner: step 2');
        return value;
      });
  })
  .then(value => {
    console.log('outer: step 2');
    console.log(value);
  });

// good
Promise.resolve('hello world')
  .then(value => {
    console.log('outer: step 1');

    // 这里不继续 then
    return Promise.resolve(value);
  })
  .then(value => {
    console.log('inner: step 1');
    return value;
  })
  .then(value => {
    console.log('inner: step 2');
    return value;
  })
  .then(value => {
    console.log('outer: step 2');
    console.log(value);
  });

// 两个函数都会输出:
// outer: step 1
// inner: step 1
// inner: step 2
// outer: step 2
// hello world

实现

构造函数

function Promise(resolver) {
  var self = this;
  self.data = undefined; // Promise 的值
  self.status = 'pending'; // Promise 当前的状态
  self.callbacks = []; // Promise 的回调函数集,格式:[{onResolved: Function, onRejected: Function}]

  // setTimeout 用于异步执行,模拟 micro Task
  function resolve(value) {
    if (self.status !== 'pending') {
      return;
    }

    self.status = 'resolved';
    self.data = value;

    for (var i = 0; i < self.callbacks.length; i++) {
      self.callbacks[i].onResolved(value);
    }
  }

  // setTimeout 用于异步执行,模拟 micro Task
  function reject(reason) {
    if (self.status !== 'pending') {
      return;
    }

    self.status = 'rejected';
    self.data = reason;

    for (var i = 0; i < self.callbacks.length; i++) {
      self.callbacks[i].onRejected(reason);
    }
  }

  // 执行 resolver 的过程中有可能出错,所以用 try/catch 包起来,出错后以 catch 到的值 reject 掉这个 Promise
  try {
    resolver(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}

Promise.prototype.then

Promise.prototype.then = function(onResolved, onRejected) {
  // 根据标准,如果 then 的参数不是 function,则我们需要忽略它,此处以如下方式处理
  // return value/reason 保证 then/catch 实参留空时,值可以“穿透”到后面
  onResolved =
    typeof onResolved === 'function'
      ? onResolved
      : function(v) {
          return v;
        };
  onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : function(r) {
          throw r;
        };

  var self = this;
  var promise2;

  // 当前 promise 状态已确定,且为 resolved
  if (self.status === 'resolved') {
    return (promise2 = new Promise(function(resolve, reject) {
      try {
        // 调用 onResolved
        var x = onResolved(self.data);
        // 根据 x 的值来决定 promise 的状态,兼容不同的 Promise 实现,如 Q、bluebird 等
        resolvePromise(promise2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    }));
  }

  // 当前 promise 状态已确定,且为 rejected
  if (self.status === 'rejected') {
    return (promise2 = new Promise(function(resolve, reject) {
      try {
        // 调用 onRejected
        var x = onRejected(self.data);
        // 根据 x 的值来决定 promise 的状态,兼容不同的 Promise 实现,如 Q、bluebird 等
        resolvePromise(promise2, x, resolve, reject);
      } catch (e) {
        reject(e);
      }
    }));
  }

  // 当前 promise 状态不确定

  // 如果当前的 Promise 还处于 pending 状态,我们并不能确定调用 onResolved 还是 onRejected,
  // 只能等到 Promise 的状态确定后,才能确实如何处理。
  // 所以我们需要把我们的两种情况的处理逻辑做为 callback 放入 promise1(此处即 this/self)的回调数组里
  if (self.status === 'pending') {
    return (promise2 = new Promise(function(resolve, reject) {
      self.callbacks.push({
        onResolved: function(value) {
          try {
            var x = onResolved(value);
            // 根据 x 的值来决定 promise 的状态,兼容不同的 Promise 实现,如 Q、bluebird 等
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        },
        onRejected: function(reason) {
          try {
            var x = onRejected(reason);
            // 根据 x 的值来决定 promise 的状态,兼容不同的 Promise 实现,如 Q、bluebird 等
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }
      });
    }));
  }
};

Promise.prototype.catch

用于指定发生错误时的回调函数。

Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

Promise.prototype.finally

用于指定不管 Promise 对象最后状态如何,都会执行的操作。

Promise.prototype.catch = function(fn) {
  return this.then(
    function(v) {
      setTimeout(fn);
      return v;
    },
    function(r) {
      setTimeout(fn);
      throw r;
    }
  );
};

Promise.resolve

用于将现有对象转为 Promise 对象,并且状态为 resolved。

Promise.resolve = function(value) {
  return new Promise(function(resolve, reject) {
    // 根据 x 的值来决定 promise 的状态,兼容不同的 Promise 实现,如 Q、bluebird 等
    resolvePromise(promise, value, resolve, reject);
  });
};

Promise.reject

用于创建一个状态为 rejected 的 Promise 对象。

Promise.reject = function(reason) {
  return new Promise(function(resolve, reject) {
    reject(reason);
  });
};

Promise.all

用于将多个 Promise 实例,包装成一个新的 Promise 实例。

Promise.all = function(promises) {
  return new Promise(function(resolve, reject) {
    var resolvedCounter = 0; // 计数器
    var promiseNum = promises.length;
    var resolvedValues = new Array(promiseNum); // 完成的 promise
    for (var i = 0; i < promiseNum; i++) {
      (function(i) {
        Promise.resolve(promises[i]).then(
          function(value) {
            resolvedCounter++;
            resolvedValues[i] = value;
            // 等待 promises 全部完成
            if (resolvedCounter === promiseNum) {
              resolve(resolvedValues);
            }
          },
          function(reason) {
            reject(reason);
          }
        );
      })(i);
    }
  });
};

注释

查看 index.js 文件

测试

npm i promises-aplus-tests -g
promises-aplus-tests ./index.js

# 或者
npm run test

参考