设计流畅的 Javascript 界面以抽象出 AJAX 的异步特性

发布于 2024-09-01 00:23:34 字数 1182 浏览 7 评论 0 原文

我将如何设计一个 API 来隐藏 AJAX 和 HTTP 请求的异步性质,或者基本上延迟它以提供流畅的界面。展示 Twitter 新 Anywhere API 的示例:

// get @ded's first 20 statuses, filter only the tweets that
// mention photography, and render each into an HTML element
T.User.find('ded').timeline().first(20).filter(filterer).each(function(status) {
    $('div#tweets').append('<p>' + status.text + '</p>');
});

function filterer(status) {
    return status.text.match(/photography/);
}

与此(每个调用的异步性质是清晰可见)

T.User.find('ded', function(user) {
    user.timeline(function(statuses) {
        statuses.first(20).filter(filterer).each(function(status) {
            $('div#tweets').append('<p>' + status.text + '</p>');
        });
    });
});

function filterer(status) {
    return status.text.match(/photography/);
}

它找到用户,获取他们的推文时间线,仅过滤前 20 条推文,应用自定义过滤器,并最终使用回调函数来处理每条推文。

我猜测像这样设计良好的 API 应该像查询构建器一样工作(想想 ORM),其中每个函数调用都会构建查询(在本例中为 HTTP URL),直到它遇到循环函数,例如each/map/等,进行 HTTP 调用,传入的函数将成为回调。

一个简单的开发路线是使每个 AJAX 调用同步,但这可能不是最好的解决方案。我有兴趣找出一种使其异步的方法,并且仍然隐藏 AJAX 的异步本质。

How would I design an API to hide the asynchronous nature of AJAX and HTTP requests, or basically delay it to provide a fluent interface. To show an example from Twitter's new Anywhere API:

// get @ded's first 20 statuses, filter only the tweets that
// mention photography, and render each into an HTML element
T.User.find('ded').timeline().first(20).filter(filterer).each(function(status) {
    $('div#tweets').append('<p>' + status.text + '</p>');
});

function filterer(status) {
    return status.text.match(/photography/);
}

vs this (asynchronous nature of each call is clearly visible)

T.User.find('ded', function(user) {
    user.timeline(function(statuses) {
        statuses.first(20).filter(filterer).each(function(status) {
            $('div#tweets').append('<p>' + status.text + '</p>');
        });
    });
});

function filterer(status) {
    return status.text.match(/photography/);
}

It finds the user, gets their tweet timeline, filters only the first 20 tweets, applies a custom filter, and ultimately uses the callback function to process each tweet.

I am guessing that a well designed API like this should work like a query builder (think ORMs) where each function call builds the query (HTTP URL in this case), until it hits a looping function such as each/map/etc., the HTTP call is made and the passed in function becomes the callback.

An easy development route would be to make each AJAX call synchronous, but that's probably not the best solution. I am interested in figuring out a way to make it asynchronous, and still hide the asynchronous nature of AJAX.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情绪少女 2024-09-08 00:23:46

我相信,AJAX 同步问题已经被 jQuery 等库抽象出来了(即它的 ajax 调用 允许您通过 async 属性指定异步或同步操作)。如果选择同步模式,则会隐藏实现的异步性质。

jQuery 也是流畅界面和链接的一个例子。还有其他库也做同样的事情。避免您重新发明轮子 - 让您立即找到您想要的东西。

如果这可以作为答案,那么您将在这些功能之间获得良好的自动浏览器兼容性。这些东西需要很长时间才能从头开始构建。

我看到 Twitter 的新 Anywhere API 注释 jQuery - 如果您进行一些挖掘,也许一切都已经在那里。

The AJAX synchronous issue, I believe, has already been abstracted away by libraries such as jQuery (i.e. its ajax call which allows you to specify async or synch operation through the async property). The synchronous mode, if chosen, hides the asynchronous nature of the implementation.

jQuery is also an example of a fluent interface and chaining. There are other libraries that do the same. Saves you reinventing the wheel - gets you rolling right away with what you are looking for.

If this works as an answer then you get some good automatic browser compatibility across these features. That stuff takes a long while to build out from scratch.

I see Twitter's new Anywhere API notes jQuery - maybe everything is already there if you do some digging.

无法言说的痛 2024-09-08 00:23:44

我正在开发 FutureJS ,它最初基于 Crockford 的承诺 (原始幻灯片)。当前的目标是成为 JavaScript 的异步工具箱并消除链式混乱。

Futures.chainify(providers, Consumers, context, params)

异步方法排队允许您对可能可用或不可用的数据进行链接操作。
这就是 Twitter 的 @Anywhere api 的工作原理。

您可能需要一个以这种方式远程获取数据的模型:

Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();

可以像这样实现:

var Contacts = Futures.chainify({
  // Providers must be promisables
  all: function(params) {
    var p = Futures.promise();
    $.ajaxSetup({ error: p.smash });
    $.getJSON('http://graph.facebook.com/me/friends', params, p.fulfill);
    $.ajaxSetup({ error: undefined });
    return p.passable();
  },
  one: function(id, params) {
    var p = Futures.promise();
    $.ajaxSetup({ error: p.smash });
    $.getJSON('http://graph.facebook.com/' + id, params, p.fulfill);
    $.ajaxSetup({ error: undefined });
    return p.passable();
  }
},{
  // Consumers will be called in synchronous order
  // with the `lastResult` of the previous provider or consumer.
  // They should return either lastResult or a promise
  randomize: function(data, params) {
    data.sort(function(){ return Math.round(Math.random())-0.5); // Underscore.js
    return Futures.promise(data); // Promise rename to `immediate`
  },
  limit: function(data, n, params) {
    data = data.first(n);
    return Futures.promise(data);
  },
  display: function(data, params) {
    $('#friend-area').render(directive, data); // jQuery+PURE
    // always return the data, even if you don't modify it!
    // otherwise your results could be unexpected
    return data;
  }
});

要知道的事情:

  • providers - 返回数据的 promisables
  • consumers - 使用和的函数或更改数据
    • 第一个参数必须是数据
    • 当返回一个 Promisable 时,链中的下一个方法将不会执行,直到 Promise 得到履行
    • 当返回“文字对象”时,链中的下一个方法将使用该对象
    • 当返回未定义(或不返回任何内容)时,链中的下一个方法将使用已定义的对象
  • context - apply()d 来每个提供者和消费者,从而成为 this 对象
  • params - 保留供将来使用

或者,您可以使用同步回调链接 - 您可能在其他地方看到过 chain().next () 或 then():

Futures.sequence(function(callback) {

    $.getJSON("http://example.com", {}, callback);

}).then(function(callback, result, i, arr) {

    var data = transform_result(result);
    $.getJSON("http://example.com", data, callback);

}).then(...)

我将其命名为 sequence 而不是 chain 因为 _.js 已经有一个名为 chain 的方法,我想也为我的库使用 _.methodName 。

看一看让我知道您的想法

FuturesJS 可以毫无问题地与 jQuery、Dojo 等一起工作。没有依赖性。它将与 Node.js 一起使用(当使用 env.js 时,还可以与 Rhino 一起使用)。

=8^

D至于 ORM / MVC 修复 - 您可以查看 JavaScriptMVCSproutCore。我也在开发自己的解决方案 TriforceJS,但我还没有准备好发布任何内容。

PPS promisables 示例

var doStuff = function (httpResult) {
    // do stuff
  },
  doMoreStuff = function (httpResult) {
    // do more stuff
  };

function fetchRemoteData(params) {
  var promise = Futures.promise();
  $.getJSON("www.example.com", params, promise.fulfill, 'jsonp');
  return promise;
}

p = fetchRemoteData(params);
p.when(doStuff);
p.when(doMoreStuff);

I'm developing FutureJS which was originally based on Crockford's promises (original slides). The current goal is to be the Async Toolbox of JavaScript and eliminate chaining clutter.

Futures.chainify(providers, consumers, context, params)

Asynchronous method queueing allows you to chain actions on data which may or may not be readily available.
This is how Twitter's @Anywhere api works.

You might want a model which remotely fetches data in this fashion:

Contacts.all(params).randomize().limit(10).display();
Contacts.one(id, params).display();

Which could be implemented like so:

var Contacts = Futures.chainify({
  // Providers must be promisables
  all: function(params) {
    var p = Futures.promise();
    $.ajaxSetup({ error: p.smash });
    $.getJSON('http://graph.facebook.com/me/friends', params, p.fulfill);
    $.ajaxSetup({ error: undefined });
    return p.passable();
  },
  one: function(id, params) {
    var p = Futures.promise();
    $.ajaxSetup({ error: p.smash });
    $.getJSON('http://graph.facebook.com/' + id, params, p.fulfill);
    $.ajaxSetup({ error: undefined });
    return p.passable();
  }
},{
  // Consumers will be called in synchronous order
  // with the `lastResult` of the previous provider or consumer.
  // They should return either lastResult or a promise
  randomize: function(data, params) {
    data.sort(function(){ return Math.round(Math.random())-0.5); // Underscore.js
    return Futures.promise(data); // Promise rename to `immediate`
  },
  limit: function(data, n, params) {
    data = data.first(n);
    return Futures.promise(data);
  },
  display: function(data, params) {
    $('#friend-area').render(directive, data); // jQuery+PURE
    // always return the data, even if you don't modify it!
    // otherwise your results could be unexpected
    return data;
  }
});

Things to know:

  • providers - promisables which return data
  • consumers - functions which use and or change data
    • the first argument must be data
    • when returning a promisable the next method in the chain will not execute until the promise is fulfilled
    • when returning a "literal object" the next method in the chain will use that object
    • when returning undefined (or not returning anything) the next method in the chain will use the defined object
  • context - apply()d to each provider and consumer, thus becoming the this object
  • params - reserved for future use

Alternatively you could use synchronous callback chaining - what you may have seen elsewhere as chain().next() or then():

Futures.sequence(function(callback) {

    $.getJSON("http://example.com", {}, callback);

}).then(function(callback, result, i, arr) {

    var data = transform_result(result);
    $.getJSON("http://example.com", data, callback);

}).then(...)

I named it sequence rather than chain since _.js already has a method named chain and I'd like to use _.methodName for my library as well.

Take a peek and let me know what you think.

FuturesJS will work alongside jQuery, Dojo, etc without issue. There are no dependencies. It will work with Node.js (and Rhino when using env.js).

=8^D

P.S. As to the ORM / MVC fix - you can check out JavaScriptMVC and SproutCore. I'm also working on my own solution called TriforceJS, but I don't have anything ready for release yet.

P.P.S Example of promisables

var doStuff = function (httpResult) {
    // do stuff
  },
  doMoreStuff = function (httpResult) {
    // do more stuff
  };

function fetchRemoteData(params) {
  var promise = Futures.promise();
  $.getJSON("www.example.com", params, promise.fulfill, 'jsonp');
  return promise;
}

p = fetchRemoteData(params);
p.when(doStuff);
p.when(doMoreStuff);
只为守护你 2024-09-08 00:23:41

请查看 Twitter 工程师 Dustin Diaz 几天前在 @anywhere 上发表的以下文章:

他谈到了一种非常好的技术,该技术允许您使用非常简单的队列实现,在异步方法上实现流畅的接口,基本上是独立于回调的方法链接在一起。

Give a look to the following article published just a couple of days ago by Dustin Diaz, Twitter Engineer on @anywhere:

He talks about a really nice technique that allows you to implement a fluent interface on asynchronous methods, basically methods chained together independent of a callback, using a really simple Queue implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文