原文:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt1-theory-and-semantics

Introduction

In the not too distant past the primary tool available to JavaScript programmers for handling asynchronous events was the callback.

A callback is a piece of executable code that is passed as an argument to other code, which is expected to call back ( execute ) the argument at some convenient time
— Wikipedia

In other words, a function can be passed as an argument to another function to be executed when it is called.

There’s nothing inherently wrong with callbacks, but depending on which environment we are programming in there are a number of options available for managing asynchronous events. In this post my goal is to examine one set of available tools: promise objects and deferred objects. In part I, we will cover theory and semantics and in part 2 we will look at the use.

One of the keys to effectively working with asynchronous events in JavaScript is understanding that the program continues execution even when it doesn’t have the value it needs for work that is in progress. Dealing with as yet unknown values from unfinished work can make working with asynchronous events in JavaScript challenging — especially when you’re first getting started.

A classic example of this would be an XMLHttpRequest ( Ajax ). Imagine we want to:

  • make an Ajax request to get some data
  • immediately do something with that data, and then
  • do other things

In our program we initiate our Ajax request. The request is made but unlike with synchronous events, execution of our program isn’t stopped while the server is responding, instead the program continues running. By the time we get the response data from our Ajax request, the program has already finished execution.

Promises & Deferreds: What are they?

Promises are a programming construct that have been around since 1976. In short:

  • a promise represents a value that is not yet known
  • a deferred represents work that is not yet finished

Considered from a high level, promises in JavaScript give us the ability to write asynchronous code in a parallel manner to synchronous code. Let’s start with a diagram to get an overview of the big picture before diving into the specifics.

A promise is a placeholder for a result which is initially unknown while a deferred represents the computation that results in the value. Every deferred has a promise which functions as a proxy for the future result. While a promise is a value returned by an asynchronous function, a deferred can be resolved or rejected by it’s caller which separates the promise from the resolver. The promise itself can be given to any number of consumers and each will observe the resolution independently meanwhile the resolver / deferred can be given to any number of producers and the promise will be resolved by the one that first resolves it. From a semantic perspective this means that instead of calling a function ( callback), we are able to return a value ( promise ).

Promises According to the Promise/A Proposal

The Promises /A Proposal suggests the following standard behavior and API regardless of implementation details.

A promise:

  • Represents the eventual value returned from the single completion of an operation
  • may be in one of 3 states: unfulfilled, fulfilled and failed and may only move from unfulfilled to either fulfilled or failed
  • has a function as a value for the property “then” ( which must return a promise)
  • Adds a fulfilledHandler, errorHandler, and progressHandler to be called for completion of a promise.

    then(fulfilledHandler, errorHandler, progressHandler)
  • The value that is returned from the callback handler is the fulfillment value for the returned promise

  • promise’s value MUST not be changed (avoids side effects from listeners creating unanticipated behavior)

In other words, stripping out some of the nuances for a moment:

A promise serves as a proxy for a future value, has 3 possible states and needs to have a function which adds handlers for it’s states: fulfilledHandler, errorHandler and progressHandler ( optional ) and returns a new promise ( to allow chaining ) which will be resolved / rejected when the handler finishes executing.

The states and return value of a promise

A promise has 3 possible states: unfulfilled, fulfilled and failed.

  • unfulfilled: since a promise is a proxy for an unknown value it starts in an unfulfilled state
  • fulfilled: the promise is filled with the value it was waiting for
  • failed: if the promise was returned an exception, it is in the failed state.

A promise may only move from unfulfilled to either fulfilled or failed. Upon resolution or rejection, any observers are notified and passed the promise / value. Once the promise has been resolved or rejected neither it’s state or the resulting value can be modified.

Here is an example of what this looks like:

// Promise to be filled with future value
var futureValue = new Promise(); // .then() will return a new promise
var anotherFutureValue = futureValue.then(); // Promise state handlers ( must be a function ).
// The returned value of the fulfilled / failed handler will be the value of the promise.
futureValue.then({ // Called if/when the promise is fulfilled
fulfilledHandler: function() {}, // Called if/when the promise fails
errorHandler: function() {}, // Called for progress events (not all implementations of promises have this)
progressHandler: function() {}
});

Implementation differences & Performance

When choosing a promise library, there are a number of considerations to take into account. Not all implementations are created equal. They can differ in regards to what utilities are offered by the API, performance and even in behaviour.

Since the Promise/A proposal only outlines a proposal for the behaviour of promises and not implementation specifics, varying promise libraries have differing sets of features. All Promise/A compliant implementations have a .then() function but also have varying features in their API. Additionally, they are still able to exchange promises with each other. jQuery is the noticeable exception to the rule because its implementation of promises is not fully Promise/A compliant. The impact of this decision is documented here and discussed here. In Promise/A compliant libraries, a thrown exception is translated into a rejection and the errorHandler() is called with the exception. In jQuery’s implementation an uncaught exception will hault the program’s execution. As a result of the differing implementation, there are interoperability problems if working with libraries which return or expect Promise/A compliant promises.

One solution to this problem is to convert jQuery promises into Promise/A compliant promises with another promise library and then use the API from the compliant library.

For example:

when($.ajax()).then()

When reading through jQuery’s decision to stick with their implementation of promises, a mention of performance considerations piqued my curiosity and I decided to do a quick performance test. I used Benchmark.js and tested the results of creating and resolving a deferred object with a success handler in .then().

The results:

jQuery 91.6kb When.js 1.04kb Q.js 8.74kb
9,979 ops/sec ±10.22% 96,225 ops/sec ±10.10% 2,385 ops/sec ±3.42%

Note: minified with Closure compiler, not gzipped

After running these tests, I discovered a much more in-depth test-suite of promise libraries which reveals similar overall results.

The differences in performance may be negligible in a real application but in my Benchmark.js tests, when.js comes out as a clear winner. This combination of speed and the small size of the library make when.js a great choice when considering performance in the equation.

Clearly there are tradeoffs to consider when choosing a promise library. The answer to which library you should use depends on your specific use case and the needs of your project. Some implementations to start exploring are:

  • When.js: Fast, lightweight implementation that has a number of useful utilities and as of v2.0 has full support for asynchronous resolution.
  • Q.js: Runs in the browser or Node.js, offers a robust API and is fully Promise/A compliant.
  • RSVP: Barebones implementation that is fully Promise/A compliant.
  • jQuery: Not Promise/A compliant but is widely used. If you are already using jQuery in your project it’s easy to get started with and worth a look.

Conclusion

Promises provide the JavaScript developer a tool for working with asynchronous events . Now that we’ve covered some of the specifics of what promise objects and deferred objects are and how they behave, we are ready to dive into the specifics of how to work with them. In part 2, we’ll take a closer look at using promises, some common gotcha’s and some specifics of the jQuery API. In the meantime, feel free to start a conversation on App.net or Hacker News.

Further Resources

Books

Articles

JSFiddle

Promise & Deferred objects in JavaScript Pt.1: Theory and Semantics.的更多相关文章

  1. Promise & Deferred Objects in JavaScript Pt.2: in Practice

    原文:http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use Intr ...

  2. based on Greenlets (via Eventlet and Gevent) fork 孙子worker 比较 gevent不是异步 协程原理 占位符 placeholder (Future, Promise, Deferred) 循环引擎 greenlet 没有显式调度的微线程,换言之 协程

    gevent GitHub - gevent/gevent: Coroutine-based concurrency library for Python https://github.com/gev ...

  3. A brief look at the Objects in JavaScript

    Objects  An object is a self-contained collection of data. This data comes in to forms:  properties ...

  4. Promise/Deferred

    [fydisk] 1.$.get('/api').success(onSuccess).error(onError).comlete(onComplete); 2.对同一事件加入多个Handler. ...

  5. 异步编程Promise/Deferred、多线程WebWorker

    长期以来JS都是以单线程的模式运行的,而JS又通常应用在操作用户界面和网络请求这些任务上.操作用户界面时不能进行耗时较长的操作否则会导致界面卡死,而网络请求和动画等就是耗时较长的操作.所以在JS中经常 ...

  6. Jquery AJAX如何使用Promise/Deferred实现顺序执行?

    有的时候有我有N个AJAX请求,第下个请求可能要依赖上个请求的返回值, 可以用 $.ajax("test1.php").then(function(data) { // data ...

  7. 细说Promise

    一.前言 JavaScript是单线程的,固,一次只能执行一个任务,当有一个任务耗时很长时,后面的任务就必须等待.那么,有什么办法,可以解决这类问题呢?(抛开WebWorker不谈),那就是让代码异步 ...

  8. javascript中的promise和deferred:实践(二)

    javascript中的promise和deferred:实践(二) 介绍: 在第一节呢,我花了大量的时间来介绍promises和deferreds的理论.现在呢,我们来看看jquery中的promi ...

  9. Javascript - Promise学习笔记

    最近工作轻松了点,想起了以前总是看到的一个单词promise,于是耐心下来学习了一下.   一:Promise是什么?为什么会有这个东西? 首先说明,Promise是为了解决javascript异步编 ...

随机推荐

  1. C语言中内存管理规范

    一.内存申请 1.建议使用calloc申请内存,尽量不要使用malloc. calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据. 2.申请内存大 ...

  2. C# 接口的隐式与显示实现及适应场景

    通俗的来讲,“显示接口实现”就是使用接口名称作为方法名的前缀;而传统的实现方式称之为:“隐式接口实现”.废话不说,例子如下: interface IAnimal { void Dog(); } cla ...

  3. FineUI表格、窗体、按钮组及事件

    //表格 @(F.Grid().IsFluid(true).CssClass("blockpanel").Title("表格").ShowHeader(true ...

  4. sql 数据库数据 批量判断修改

    A表B表相关联  更新B表中的VisitWeek字段值 CCD_PartnerVisit 此为B表 Dell_FiscalWeek  此为A表 UPDATE CCD_PartnerVisit SET ...

  5. 微信小程序学习资料整理

    基础篇 官网: https://mp.weixin.qq.com/cgi-bin/wx 微信小程序: 小程序是一种新的开放能力,开发者可以快速地开发一个小程序.小程序可以在微信内被便捷地获取和传播,同 ...

  6. SDWebImage实现图片展示、缓存、清除缓存

    1. /* 图片显示 */ [self.imageView sd_setImageWithURL:[NSURL URLWithString:urlString]];                [s ...

  7. Java的Collection.sort()方法

    Java中的Collection.sort()方法能使用泛型对对象的变量进行排序,下面是两种方法. 文件名:student.java import java.util.*; import com.su ...

  8. item2 快捷键

    快捷键 ⌘ + Click:可以打开文件,文件夹和链接 ⌘ + n:新建窗口 ⌘ + t:新建标签页 ⌘ + w:关闭当前页 ⌘ + 数字 & ⌘ + 方向键:切换标签页 ⌥⌘ + 数字:切换 ...

  9. JVM的逃逸分析

    我们都知道Java中的对象默认都是分配到堆上,在调用栈中,只保存了对象的指针.当对象不再使用后,需要依靠GC来遍历引用树并回收内存.如果堆中对象数量太多,回收对象还有整理内存,都会会带来时间上的消耗, ...

  10. --num 与 num-- 的区别

    递增++和递减--操作符都属于一元操作符. 只能操作一个值的运算符是一元操作符,一元操作符是ECMscript中最简单的操作符. 递增.递减操作符介绍 递增.递减操作符有两个版本:前置型和后置型.顾名 ...