Opened 8 years ago

Closed 7 years ago

Last modified 7 years ago

#14615 closed task (fixed)

New Deferred API

Reported by: Mark Wubben Owned by: Mark Wubben
Priority: high Milestone: 1.8
Component: Core Version:
Keywords: Cc: Kris Zyp, bill
Blocked By: Blocking:

Description (last modified by Mark Wubben)

Dojo 1.8 will have a new Deferred API available under dojo/Deferred, with a when() implementation under dojo/when. dojo.Deferred will remain dojo/_base/Deferred. dojo/_base/Deferred also sets dojo.when, which *is the same as dojo/when. Instances of dojo/_base/Deferred have a promise object that is an instance of dojo/promise/Promise`, i.e. a new promise object.

Notable changes:

  • Based on Promises/A, ergo no support for addCallback, callback etc.
  • You can resolve/reject/progress/cancel a deferred multiple times without errors being thrown, unless a "strict" flag is set.
  • resolve/reject/progress return the promise.
  • a reason can be passed to cancel() for communicating to the canceler why the deferred needs to be canceled.
  • when() without callbacks always returns a promise.

Additional changes:

  • when() converts foreign promises.
  • isResolved/isRejected/isCanceled/isFulfilled helpers on deferreds and promises.
  • fail/both helpers on promises.
  • custom CancelError subclass of Error.
  • To detect errors (or generally observe the behavior of promises) a tracing mechanism has been added. This replaces logging errors to the console in dojo/_base/Deferred.
  • Introduces dojo/promise/first and dojo/promise/all as a replacement for dojo/DeferredList. The later is unmodified.

Change History (33)

comment:1 Changed 8 years ago by Mark Wubben

Description: modified (diff)

comment:2 Changed 8 years ago by Mark Wubben

In [28265]:

Land new Promise API, refs #14615 !strict

comment:3 Changed 8 years ago by Mark Wubben

Status: newassigned

The API has landed, work remains on documentation.

comment:4 Changed 8 years ago by bill

In [28272]:

Use dojo/when rather than dojo/_base/Deferred (for dijit), refs #14615 !strict. For _AutoCompleterMixin.js it was just including Deferred unnecessarily, so removed it.

comment:5 Changed 8 years ago by Mark Wubben

In [28351]:

Remove use of named functions, refs #15179 !strict.

Linting fixes, refs #14615 !strict.

comment:6 Changed 8 years ago by Mark Wubben

In [28372]:

Rename dojo/promise/Promise#both() to dojo/promise/Promise#always(). Refs #14615 !strict

comment:7 Changed 7 years ago by Mark Wubben

In [28526]:

When chaining promises, immediately signal deferred for non-promise or fulfilled-promise results.

Refs #15297, #14615 !strict

comment:8 Changed 7 years ago by liucougar

can we add back config.deferredOnError support to dojo/_base/Deferred?

AFAICT, with the new implementation, there is no way to setup a default error catcher: promise/tracer has to be required, and then trace() has to be called on every deferred created

IMO, removing config.deferredOnError support from dojo/_base/Deferred breaks backward compatibility.

comment:9 Changed 7 years ago by bill

In [28612]:

Document dojo/_base/Deferred as deprecated, and minimize the number of references to the dojo global, refs #14615 !strict

comment:10 in reply to:  8 Changed 7 years ago by Mark Wubben

Replying to liucougar:

can we add back config.deferredOnError support to dojo/_base/Deferred?

IMO, removing config.deferredOnError support from dojo/_base/Deferred breaks backward compatibility.

I suppose you could argue that, though it's only a change during development. But keeping it may just make it harder to debug, since new promises aren't affected. That could be rather confusing.

AFAICT, with the new implementation, there is no way to setup a default error catcher: promise/tracer has to be required, and then trace() has to be called on every deferred created

Correct. What functionality are you expecting from a "default error catcher"? I've found trace() to be quite useful during debugging but I don't typically need something that alerts me to all errors, especially since throwing errors itself is a vital part of control flow, not necessarily an exception that must be logged.

comment:11 Changed 7 years ago by liucougar

we use "default error catcher" for logging purposes on production: whenever uncatched exception happens, we would log the exception

tracing could do this, but the problem is we have to modify lots of code (some are actually in dojo/dijit) to add the call to tracing for each deferred created, which sounds infeasible

comment:12 Changed 7 years ago by Mark Wubben

But the problem is, not all rejected promises are errors that need to be logged. It sounds like you need to log unexpected errors at the end of the promise chain. And although tracing shouldn't have side-effects if the tracer module is not included, it's not necessarily meant for production code.

comment:13 Changed 7 years ago by liucougar

I agree not all rejected promises are errors. however, in case someone wants to catch these errors, there used to be a way to do it. Currently, there is no easy way to do it any more.

I am ok with some other approaches, such as: expose Deferred.prototype so we could monkey patch it to add back the original behavior

comment:14 Changed 7 years ago by Mark Wubben

It can't be monkey-patched either. One option might be to add some sugar to dojo/promise/Promise to add say logError() which you could call at the end of your promise chains so it logs the error. But that'd be custom per application I think. What do you think?

comment:15 Changed 7 years ago by liucougar

my goal is to have a single place to capture all errors on deferred which do not have any errback defined

I don't want to modify all places in our code base to add errback for each deferred created, or call a function whenever a deferred is created.

could you think of someway to achieve that? (I'd like the solution to also work on the new promise if possible)

comment:16 Changed 7 years ago by Mark Wubben

In [28672]:

Change `Promise#fail()` to `Promise#otherwise()`

"otherwise" is a noun and not a verb, and nicely mirrors `Promise#always()`. Adds API compatibility with when.js.

comment:17 Changed 7 years ago by Mark Wubben

In [28719]:

Revert signal queue, recursively resolve promise chains

While nice in theory, using a signal queue breaks expectations around interacting with resolved promises.

Fixes #14685, refs #14615 !strict

comment:18 Changed 7 years ago by Mark Wubben

In [28748]:

Add dojo/errors to house custom errors.

dojo/errors/create can be used to create new errors. dojo/errors/CancelError is the first custom error, moved from dojo/promise/CancelError.

Refs #14615 !strict

comment:19 Changed 7 years ago by dylan

In [28751]:

refs #14615, unbreak trunk until markwubben adds his missing file

comment:20 Changed 7 years ago by Mark Wubben

In [28755]:

Also add dojo/errors/create

Refs #14615 !strict

comment:21 Changed 7 years ago by Mark Wubben

In [28756]:

Restore CancelError?

Refs #14615 !strict

comment:22 Changed 7 years ago by Bryan Forbes

In [28759]:

Add dojoType to CancelError? and make module reference in dojo/errors/create relative. refs #14615 !strict

comment:23 Changed 7 years ago by Mark Wubben

In [28947]:

Instrumenting deferreds to report unhandled rejection values.

Loading dojo/promise/instrumenting!report-rejections immediately logs unhandled rejection values to the console.

Use dojo/promise/instrumenting!report-unhandled-rejections to only log rejection values that are unhandled after a short timeout (default 1s). Note that this works by comparing error objects, so make sure the rejection values are all unique.

Also supports the deprecated dojo/_base/Deferred.

When supported by the JavaScript? engine, stack traces are exposed for deferreds and promises and logged with the rejection values. Stack traces are not exposed by the deprecated dojo/_base/Deferred instances.

Refs #14615 !strict

comment:24 Changed 7 years ago by Mark Wubben

In [28948]:

Enable reporting of unhandled rejections when loading from source

When using a non-built Dojo, unhandled rejections are automatically reported.

Refs #14615 !strict

comment:25 Changed 7 years ago by Mark Wubben

In [28949]:

Disable instrumenting deferreds in default build outputs

Refs #14615 !strict

comment:26 Changed 7 years ago by bill

Probably this should be marked as fixed?

comment:27 Changed 7 years ago by Mark Wubben

Resolution: fixed
Status: assignedclosed

Sure.

I can still attach further changes (e.g. some tweaks to instrumenting) to this ticket?

comment:28 Changed 7 years ago by bill

Sure, that's fine, at least until 1.8 is released. (After that, it's good to file new tickets for new features or newly discovered bugs.)

comment:29 Changed 7 years ago by Mark Wubben

In [29170]:

Log original error, as well as any stack trace

Refs #14615, #15579 !strict

comment:30 Changed 7 years ago by Mark Wubben

In [29171]:

Update promise/instrumenting to automatically set up the tracer

Refs #14615 !strict

comment:31 Changed 7 years ago by liucougar

In [29175]:

refs #14615: use CancelError? in old Deferred.cancel if no reason is specified

this is inline with the new Deferred cancel behavior

!strict
--Teis line, and those below, will be ignored--

M Deferred.js

comment:32 Changed 7 years ago by bill

In [29325]:

Mark DeferredList as deprecated in the API doc, but don't call dojo.deprecated() because internal dojo code is still using it. Refs #14615 !strict.

comment:33 Changed 7 years ago by Mark Wubben

In [29367]:

Better docs for new dojo/promise. Refs #14615 !strict

Note: See TracTickets for help on using tickets.