承诺/推迟库是如何实现的?

Der*_*ang 74 javascript promise

q这样的promise/defer库是如何实现的?我试图阅读源代码,但发现它很难理解,所以我认为如果有人能从高层次向我解释在单线程JS环境中用于实现promise的技术是多么好像Node和浏览器.

Kai*_*izo 148

我发现解释比展示一个例子更难,所以这里是一个非常简单的延迟/承诺的实现.

免责声明:这不是一个功能实现,并且缺少Promise/A规范的某些部分,这只是为了解释承诺的基础.

tl; dr:转到Create classes and example部分以查看完整实现.

诺言:

首先,我们需要使用一组回调创建一个promise对象.我将开始使用对象,因为它更清晰:

var promise = {
  callbacks: []
}
Run Code Online (Sandbox Code Playgroud)

现在使用该方法添加回调:

var promise = {
  callbacks: [],
  then: function (callback) {
    callbacks.push(callback);
  }
}
Run Code Online (Sandbox Code Playgroud)

我们也需要错误回调:

var promise = {
  okCallbacks: [],
  koCallbacks: [],
  then: function (okCallback, koCallback) {
    okCallbacks.push(okCallback);
    if (koCallback) {
      koCallbacks.push(koCallback);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

推迟:

现在创建将具有承诺的延迟对象:

var defer = {
  promise: promise
};
Run Code Online (Sandbox Code Playgroud)

延期需要解决:

var defer = {
  promise: promise,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },
};
Run Code Online (Sandbox Code Playgroud)

需要拒绝:

var defer = {
  promise: promise,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },

  reject: function (error) {
    this.promise.koCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(error)
      }, 0);
    });
  }
};
Run Code Online (Sandbox Code Playgroud)

请注意,在超时中调用回调以允许代码始终是异步的.

这就是基本推迟/承诺实施所需要的.

创建类和示例:

现在让我们将两个对象转换为类,首先是promise:

var Promise = function () {
  this.okCallbacks = [];
  this.koCallbacks = [];
};

Promise.prototype = {
  okCallbacks: null,
  koCallbacks: null,
  then: function (okCallback, koCallback) {
    okCallbacks.push(okCallback);
    if (koCallback) {
      koCallbacks.push(koCallback);
    }
  }
};
Run Code Online (Sandbox Code Playgroud)

现在推迟:

var Defer = function () {
  this.promise = new Promise();
};

Defer.prototype = {
  promise: null,
  resolve: function (data) {
    this.promise.okCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(data)
      }, 0);
    });
  },

  reject: function (error) {
    this.promise.koCallbacks.forEach(function(callback) {
      window.setTimeout(function () {
        callback(error)
      }, 0);
    });
  }
};
Run Code Online (Sandbox Code Playgroud)

这是一个使用示例:

function test() {
  var defer = new Defer();
  // an example of an async call
  serverCall(function (request) {
    if (request.status === 200) {
      defer.resolve(request.responseText);
    } else {
      defer.reject(new Error("Status code was " + request.status));
    }
  });
  return defer.promise;
}

test().then(function (text) {
  alert(text);
}, function (error) {
  alert(error.message);
});
Run Code Online (Sandbox Code Playgroud)

如您所见,基本部件简单而小巧.当您添加其他选项时,它会增长,例如多个承诺解析:

Defer.all(promiseA, promiseB, promiseC).then()
Run Code Online (Sandbox Code Playgroud)

或承诺链接:

getUserById(id).then(getFilesByUser).then(deleteFile).then(promptResult);
Run Code Online (Sandbox Code Playgroud)

要了解有关规范的更多信息:CommonJS Promise Specification.请注意,主库(Q,when.js,rsvp.js,node-promise,...)遵循Promises/A规范.

希望我足够清楚.

编辑:

正如评论中所说,我在这个版本中添加了两件事:

  • 无论它具有何种状态,都可以随时调用一个承诺.
  • 链接承诺的可能性.

为了能够在解决时调用promise,您需要将状态添加到promise中,并在调用then时检查该状态.如果状态已解决或被拒绝,则只需使用其数据或错误执行回调.

为了能够链接承诺,您需要为每次调用生成一个新的延迟,then并且当承诺被解决/拒绝时,使用回调的结果解析/拒绝新的承诺.因此,当promise完成后,如果回调返回一个新的promise,它将被绑定到随之返回的promise then().如果不是,则使用回调的结果解决承诺.

这是承诺:

var Promise = function () {
  this.okCallbacks = [];
  this.koCallbacks = [];
};

Promise.prototype = {
  okCallbacks: null,
  koCallbacks: null,
  status: 'pending',
  error: null,

  then: function (okCallback, koCallback) {
    var defer = new Defer();

    // Add callbacks to the arrays with the defer binded to these callbacks
    this.okCallbacks.push({
      func: okCallback,
      defer: defer
    });

    if (koCallback) {
      this.koCallbacks.push({
        func: koCallback,
        defer: defer
      });
    }

    // Check if the promise is not pending. If not call the callback
    if (this.status === 'resolved') {
      this.executeCallback({
        func: okCallback,
        defer: defer
      }, this.data)
    } else if(this.status === 'rejected') {
      this.executeCallback({
        func: koCallback,
        defer: defer
      }, this.error)
    }

    return defer.promise;
  },

  executeCallback: function (callbackData, result) {
    window.setTimeout(function () {
      var res = callbackData.func(result);
      if (res instanceof Promise) {
        callbackData.defer.bind(res);
      } else {
        callbackData.defer.resolve(res);
      }
    }, 0);
  }
};
Run Code Online (Sandbox Code Playgroud)

推迟:

var Defer = function () {
  this.promise = new Promise();
};

Defer.prototype = {
  promise: null,
  resolve: function (data) {
    var promise = this.promise;
    promise.data = data;
    promise.status = 'resolved';
    promise.okCallbacks.forEach(function(callbackData) {
      promise.executeCallback(callbackData, data);
    });
  },

  reject: function (error) {
    var promise = this.promise;
    promise.error = error;
    promise.status = 'rejected';
    promise.koCallbacks.forEach(function(callbackData) {
      promise.executeCallback(callbackData, error);
    });
  },

  // Make this promise behave like another promise:
  // When the other promise is resolved/rejected this is also resolved/rejected
  // with the same data
  bind: function (promise) {
    var that = this;
    promise.then(function (res) {
      that.resolve(res);
    }, function (err) {
      that.reject(err);
    })
  }
};
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,它已经增长了很多.

  • 顺便问一下,很棒的答案. (5认同)
  • 你的例子绝对不行.承诺状态既不会解决(你的同时也可以被填满和拒绝),也不会在解决工作后添加回调,也不会"返回"另一个承诺(这是必不可少的). (2认同)
  • 这是一个极其简单的例子,而不是一个功能齐全的实现.我知道规范的某些部分没有实现,它可能不起作用,但它只是解释了承诺的基础. (2认同)

For*_*say 7

Q在实现方面是一个非常复杂的promise库,因为它旨在支持流水线和RPC类型的场景.我有我自己的非常裸露的骨头实现的承诺/ A +规格在这里.

原则上它很简单.在解决/解决了promise之前,您可以通过将其推入数组来记录任何回调或错误.当承诺得到解决后,您可以调用适当的回调或错误,并记录承诺的结果(以及是否已履行或拒绝).在它结束后,你只需用存储的结果调用回调或错误.

这给了你大概的语义done.要构建then你只需要返回一个新的promise,它会通过调用callbacks/errbacks来解决.

如果您对完整的承诺实现的开发有充分的理解,并且支持RPC和像Q这样的流水线,那么您可以在这里阅读kriskowal的推理.如果你正在考虑实施承诺,这是一个非常好的渐进式方法,我不能高度推荐.即使你只是要使用一个promise库,它也许值得一读.

  • 哇,[@KrisKowal]的那篇文章(http://stackoverflow.com/users/42586/kris-kowal)很棒.他应该发布它作为答案,以获得数十个赞成票:-) (2认同)

Kri*_*wal 6

正如福布斯在他的回答中提到的那样,我记录了制作像Q这样的库所涉及的许多设计决策,这里是https://github.com/kriskowal/q/tree/v1/design.可以这么说,有一个承诺库的级别,以及许多停止在各个级别的库.

在第一级,由Promises/A +规范捕获,promise是最终结果的代理,适用于管理"本地异步".也就是说,它适合于确保以正确的顺序进行工作,并且确保操作的结果简单直接,无论其是否已经结算,或将来是否会发生.它还使一方或多方订阅最终结果同样简单.

问:正如我已经实现的那样,它提供了作为最终,远程或最终+远程结果的代理的承诺.为此,它的设计是颠倒的,具有承诺延迟承诺,履行承诺,拒绝承诺和远程对象承诺的不同实现(最后一个在Q-Connection中实现).它们都共享相同的接口,并通过发送和接收"then"(这对Promises/A +已足够)以及"get"和"invoke"等消息来工作.因此,Q是关于"分布式异步",并且存在于另一层上.

然而,Q实际上是从较高层进行的,其中承诺用于管理诸如你,商人,银行,Facebook,政府 - 不是敌人,甚至是朋友之类的相互可疑方之间的分布式异步,但有时会发生冲突.利益.我实现的Q被设计成API与强化安全承诺兼容(这是分离的原因promiseresolve),希望这将人们介绍的承诺,在使用此API训练他们,并让他们把他们的代码如果他们将来需要在安全的mashup中使用promises,请与他们一起使用.

当然,当你向上移动时,通常会以速度进行权衡.因此,promises实现也可以设计为共存.这就是"可以"的概念进入的地方.每层的Promise库可以设计为使用来自任何其他层的promise,因此多个实现可以共存,用户只能购买他们需要的东西.

所有这些都说,没有理由难以阅读.Domenic和我正在开发一个Q版本,它将更加模块化和平易近人,其中一些分散注意力的依赖关系和解决方案转移到其他模块和包中.值得庆幸的是福布斯,克罗克福德等人通过制作更简单的图书馆填补了教育空白.