一个极简单的Promise实现如何工作

时间:2018-08-17 13:43:46

标签: javascript asynchronous promise

我正在通读promise的内部运作this article。为此,作者展示了promise的简化实现方式。

代码如下:

class PromiseSimple {
  constructor(executionFunction) {
    this.promiseChain = [];
    this.handleError = () => {};

    this.onResolve = this.onResolve.bind(this);
    this.onReject = this.onReject.bind(this);

    executionFunction(this.onResolve, this.onReject);
  }

  then(onResolve) {
    this.promiseChain.push(onResolve);

    return this;
  }

  catch(handleError) {
    this.handleError = handleError;

    return this;
  }

  onResolve(value) {
    let storedValue = value;

    try {
      this.promiseChain.forEach((nextFunction) => {
         storedValue = nextFunction(storedValue);
      });
    } catch (error) {
      this.promiseChain = [];

      this.onReject(error);
    }
  }

  onReject(error) {
    this.handleError(error);
  }
}

就像常规的promise一样被称为

// Assume this is your AJAX library. Almost all newer
// ones return a Promise Object
const makeApiCall = () => {
  return new PromiseSimple((resolve, reject) => {
    // Use a timeout to simulate the network delay waiting for the response.
    // This is THE reason you use a promise. It waits for the API to respond
    // and after received, it executes code in the `then()` blocks in order.
    // If it executed is immediately, there would be no data.
    setTimeout(() => {
      const apiResponse = fakeApiBackend();

      if (apiResponse.statusCode >= 400) {
        reject(apiResponse);
      } else {
        resolve(apiResponse.data);
      }
    }, 5000);
  });
};

我很难掌握以下内容:

  1. 为什么6类的行7PromiseSimple存在?我试图理解将this.onResolve绑定到this的意义。它是否已经绑定到正确的this上下文?

  2. 我不明白为什么此实现不会阻塞主线程。 PromiseSimple类中的任何地方都不会将工作转移到另一个线程或任何此类线程上。但是可以肯定的是,如果我在程序的末尾放置一个console.log(...)语句,则该console.log(...)语句将被打印为我期望的第一件事,就像常规的{{1 }}。我认为它会暂停直到伪造的promise函数完成,因为这不是makeApiCall的真实实现。

我想了解这一点,但是我只是不了解这个小型实现如何允许我们习惯的正确promise行为。任何帮助将不胜感激。


更新

已经提出了将其设为duplicate的建议,但是我想详细说明为什么它们不一样。 duplicate question是关于为什么需要异步调用的更高级的理论。我了解它们的用途,重要性以及实现它们的各种方式。我想了解其幕后工作原理。

2 个答案:

答案 0 :(得分:2)

  1. onResolveonReject必须绑定,以防止executionFunction在其他上下文或任何上下文中应用它们。 (如果出于任何原因在另一个上下文中调用resolvereject,它都必须绑定到PromiseSimple,否则this会引用其他内容或不会引用到没有上下文的任何东西)。

如果您不绑定onResolveonReject,那么以下示例将不起作用:

const makeApiCall = () => {
    return new PromiseSimple((resolve, reject) => {
        Promise.resolve().then(resolve); // Using native promise here : resolve is called without context and won't work if not bound
    });
}
  1. 此实现确实会阻塞主线程,但由于使用的是setTimeout,它可能会延迟事件循环中的执行,因此您可能看不到它。真正的Promise实现会延迟您在thencatch回调中定义的任务作为事件循环中的微任务。 (关于浏览器事件循环,这很有意思article)。

希望这会有所帮助,

答案 1 :(得分:-1)

关于1,我们来看一个独立的示例

execute = func  => func()
class X {
    xfunc() {return this;}
}
let x = new X();
execute(x.xfunc) // undefined
execute(x.xfunc.bind(x)) // x
execute(x.xfunc.bind(5)) // 5

通常,当您引用诸如x.xfunc之类的函数时,它失去了它的上下文。通常这不是问题,因为您通常不调用这样的对象函数,而是直接调用x.xfunc()。所有对象都一样,不仅仅是使用classnew语法创建的对象。

let x = {xfunc: function() {return this}}
x.xfunc() // x
execute(x.xfunc) // window
execute(x.xfunc.bind(x)) // x

现在,有了箭头功能,情况有所不同

  

箭头函数表达式的语法比函数表达式短,并且没有自己的this,arguments,super或new.target   MDN

let x = {xfunc: () => this}
x.xfunc() // window
execute(x.xfunc) // window
execute(x.xfunc.bind(x)) // window

关于2,它实际上只是回调。考虑下面的例子

let callback = () => { console.log('callback') }
let promise = { resolve: () => { callback() } }
console.log('this is not blocking');

现在,很明显,没有任何障碍,例如即使我们从未通过promise.resolve()解决诺言,也会打印控制台日志。许诺实际上只是上面的语法糖,还有一些额外的链接逻辑。