CodeCatalyst/promise-as3

语言: ActionScript

git: https://github.com/CodeCatalyst/promise-as3

ActionScript 3.0中的Promises / A +兼容实现
Promises/A+ compliant implementation in ActionScript 3.0
README.md (中文)

关于

promise-as3是Promises / A +规范的ActionScript 3.0实现。它的实现源自promise.coffee面向对象的CoffeeScript参考实现。

它是完全异步的,确保onFulfilled和onRejected回调不会在事件循环的同一回合中执行,而不是调用它们所注册的then()。

它支持回调返回的外部promise,只要它们支持标准的Promise then()方法签名。

内部解剖学

此实现将Promise功能分解为四个类:

诺言

承诺代表着未来的价值;即,可能尚未获得的值。

Promise的then()方法用于指定onFulfilled和onRejected回调,这些回调将在未来值可用时得到通知。这些回调随后可以转换已实现的值或被拒绝的原因。每次调用then()都会返回该转换值的新Promise;即,使用回调返回值实现的Promise,或者由回调引发的任何错误拒绝的Promise。

Promise类还提供了基于then()方法功能的辅助方法,包括:

  • otherwise() - 添加onRejected回调的简写;
  • always() - 添加一个onCompleted回调,类似于try..catch..finally块中的finally;
  • done() - 终止Promise链,将任何未处理的拒绝重新抛出为错误;
  • cancel() - 从Promise(消费者)而不是延迟(生产者)发起的专门拒绝;和
  • log() - 记录Promise的解析或拒绝,带有可选的类别和标识符。

递延

延迟通常在执行异步操作的函数体内使用。当该操作成功时,应该解决延期;如果该操作失败,则应拒绝延期。

通过使用可选值调用其resolve()方法来解析延迟,并通过使用可选原因调用其reject()方法来拒绝延迟。一旦延期已完成或被拒绝,它将被视为完整,并且将忽略对resolve()或reject()的后续调用。

延迟是用于创建新Promise的机制。 Deferred具有单个关联的Promise,可以安全地返回给外部使用者,以确保它们不会干扰延迟操作的解决或拒绝。

解决

Deferreds在内部使用解析器来创建,解析和拒绝Promise,以及传播履行和拒绝。

开发人员永远不会直接与解析器交互。

每个Deferred都有一个关联的Resolver,每个Resolver都有一个关联的Promise。延迟委托对其解析器的resolve()和reject()方法的resolve()和reject()调用。 Promise委托then()调用其Resolver的then()方法。通过这种方式,对Resolver操作的访问权限分为生产者(Deferred)和消费者(Promise)角色。

当调用Resolver的resolve()方法时,它会使用可选的指定值来实现。如果使用then-able(即带有then()函数的函数或对象,例如另一个Promise)调用resolve(),它会同化当时的结果;解析器在调用then-able的then()函数时提供自己的resolve()和reject()方法作为onFulfilled或onRejected参数。如果在调用then-able的then()函数时抛出错误(在任何调用指定的resolve()或reject()方法之前),Resolver会拒绝该错误。如果使用自己的Promise调用Resolver的resolve()方法,它会拒绝TypeError。

当调用Resolver的reject()方法时,它会以可选的指定原因拒绝。

每次调用Resolver的then()方法时,它都会捕获一对可选的onFulfilled和onRejected回调,并返回由这些回调转换的Resolver未来值的Promise。

后果

解析器在内部使用后果来捕获和通知回调,并将其转换后的结果传播为履行或拒绝。

开发人员永远不会直接与后果交互。

结果在两个解析器之间形成一个链,其中第一个解析器的结果在应用于第二个解析器之前由相应的回调转换。

每次调用Resolver的then()方法时,它都会创建一个新的Consequence,一旦其原始Resolver被完成或拒绝,它将被触发。结果捕获一对可选的onFulfilled和onRejected回调。

每个后果都有自己的解析器(后者又有一个Promise),在触发后果时解析或拒绝。当一个Consequence由其原始Resolver触发时,它会调用相应的回调并将转换后的结果传播到它自己的Resolver;使用回调返回值解析或拒绝回调引发的任何错误。

API

创造,解决和拒绝承诺

创建延期:

import com.codecatalyst.promise.Deferred;

...

var deferred:Deferred = new Deferred();

解决延迟:

deferred.resolve( value );

或者,拒绝推迟:

deferred.reject( reason );

获取与延迟相关的承诺传递给外部消费者:

import com.codecatalyst.promise.Promise;

...

var promise:Promise = deferred.promise;

添加回调,链接和转换

添加(可选)onFullfilled和onRejected回调到该承诺:

promise.then( onFulfilled, onRejected );

这些回调随后可以转换已实现的值或被拒绝的原因。每次调用then()都会返回该转换值的新Promise;即,使用回调返回值实现的Promise,或者由回调引发的任何错误拒绝的Promise。

或者,使用简写方法添加onRejected回调:

promise.otherwise( onRejected );

这相当于:

promise.then( null, onRejected );

因为then()返回一个新的Promise,所以可以链接这些语句:

promise
    .then( parseData )
    .then( populateUI )
    .otherwise( recover );

无论Promise是已解析还是被拒绝,都会添加一个执行的onCompleted回调:

promise
    .then( parseData )
    .then( populateUI )
    .otherwise( recover )
    .always( cleanup );

always()最终类似于try..catch..finally块。

消除

取消是一种特殊的拒绝形式,可以从Promise(消费者)而不是Deferred(生产者)发起。如果Promise仍处于未决状态,则调用其cancel()方法会触发CancellationError的拒绝,该CancellationError将传播到源自该Promise的任何Promise。

注意:取消仅传播到从目标Promise分支的Promises。它不会遍历回父分支,因为它会拒绝其他Promise可能已经分支的节点,从而导致意外的副作用。

未处理的拒绝

与基于Promise的API交互的一个缺陷是,除非指定了明确的拒绝回调,否则将无声地吞下重要错误的倾向。

例如:

promise
    .then( function () {
        // logic in your callback throws an error and it is interpreted as a rejection.
        throw new Error( 'Boom!' );
    });

// The error is silently swallowed.

通过使用done()方法终止Promise链可以解决此问题:

promise
    .then( function () {
        // logic in your callback throws an error and it is interpreted as a rejection.
        throw new Error( 'Boom!' );
    })
    .done();

// The error is thrown on the next tick of the event loop.

done()方法确保将任何未处理的拒绝重新抛出为错误。

注意:done()方法只应由Promise链的终止节点处的Promise返回API的使用者使用。返回Promise的API永远不应该在内部使用done()来为它打算返回给消费者的Promise;否则,Promise的拒绝将作为RTE抛出,并且实际上不会传播给消费者。

记录

log()方法提供了一种以编程方式在Promise链中引入日志记录的简单方法。

var category:String = "DataLoader";

dataService
    .load( id )
    .log( category )
    .then( parseData )
    .log( category, "Data parsing" )
    .then( populateUI )
    .log( category, "UI population" );

可选类别和标识符旨在合并到生成的日志条目中。

防爆。

[DEBUG] DataLoader: Promise resolved with value: <value>
[DEBUG] DataLoader: Data parsing resolved with value: <value>
[ERROR] DataLoader: UI population rejected with reason: <reason>

可以使用具有以下函数签名的自定义记录器函数配置Promise类:

function log( category:String, level:int, message:String, ...parameters ):void {
    // ...
}

自定义记录器功能可用于与日志记录框架集成。

promise-as3包括示例自定义记录器功能:

TraceLogger是一个自定义记录器功能,通过trace()记录消息。

要注册此记录器:

import com.codecatalyst.promise.logger.TraceLogger;

...

// This only needs to be done once within your application.
Promise.registerLogger(TraceLogger.log);

FlexLogger是一个自定义记录器功能,通过Flex的mx.logging.Log记录消息。

import com.codecatalyst.promise.logger.FlexLogger;

...

// This only needs to be done once within your application.
Promise.registerLogger(FlexLogger.log);

可以注册多个自定义记录器功能。可以使用unregisterLogger()方法取消注册记录器:

Promise.unregisterLogger(TraceLogger.log);
// or
Promise.unregisterLogger(FlexLogger.log);

适应即时值,Promises和AsyncTokens

可以使用Promise.when()辅助方法调整立即值,外部Promises(即来自另一个Promises / A实现的Promise)和AsyncTokens。

要适应即时价值:

var promise:Promise = Promise.when( 123 );

适应外国承诺:

var promise:Promise = Promise.when( foreignPromise );

要适应AsyncToken:

import com.codecatalyst.promise.adapters.AsyncTokenAdapter;

...

// NOTE: Only need to do this once for the entire application.
Promise.registerAdapter( AsyncTokenAdapter.adapt );

var token:AsyncToken = ...

var promise:Promise = Promise.when( token );

参考和阅读

  • 常见的JS承诺/规范
  • 承诺/ A +规范
  • 你错过了承诺点

致谢

  • Kris Zyp,他提出了原始的Common JS Promises / A Specification并创建了node-promise和promised-io;
  • Domenic Denicola承诺/ A +规范和承诺/ A +合规性测试套件,以及他的工作:
  • Kris Kowal创建了q,这是一个JavaScript承诺库,它开创了许多现在在Promises / A +规范中编写的实践;
  • Brian Cavalier对Promises / A +规范和Promise / A +一致性测试套件的贡献,以及avow.js和when.js(与John Hann)以及过去的GitHub问题讨论所提供的灵感;
  • Shaun Smith,他写了一个类似AS3的promise.coffee端口;
  • Yehor Lvivski,其Davy Jones Promises / A +实施以及我们关于优化其性能的讨论激发了对promise.coffee和promise-as3实施的改进;和
  • Jason Barry设计了promise-as3标志。

执照

版权所有(c)2013 CodeCatalyst,LLC

特此授予任何获得本软件和相关文档文件(“软件”)副本的人免费许可,无限制地交易本软件,包括但不限于使用,复制,修改,合并的权利根据以下条件,出版,分发,再许可和/或出售本软件的副本,并允许向其提供本软件的人员这样做:

上述版权声明和本许可声明应包含在本软件的所有副本或实质部分中。

本软件按“原样”提供,不提供任何明示或暗示的保证,包括但不限于适销性,特定用途的适用性和不侵权的保证。在任何情况下,作者或版权所有者均不对任何索赔,损害或其他责任承担任何责任,无论是在合同,侵权或其他方面的行为,是由于,是否与本软件或其中的使用或其他交易有关。软件。

本文使用googletrans自动翻译,仅供参考, 原文来自github.com

en_README.md

About

promise-as3 is an ActionScript 3.0 implementation of the Promises/A+ Specification. Its implementation is derived from the promise.coffee object oriented CoffeeScript reference implementation.

It is fully asynchronous, ensuring that the onFulfilled and onRejected callbacks are not executed in the same turn of the event loop as the call to then() with which they were registered.

It supports foreign promises returned by callbacks as long as they support the standard Promise then() method signature.

Internal Anatomy

This implementation decomposes Promise functionality into four classes:

Promise

Promises represent a future value; i.e., a value that may not yet be available.

A Promise's then() method is used to specify onFulfilled and onRejected callbacks that will be notified when the future value becomes available. Those callbacks can subsequently transform the value that was fulfilled or the reason that was rejected. Each call to then() returns a new Promise of that transformed value; i.e., a Promise that is fulfilled with the callback return value or rejected with any error thrown by the callback.

The Promise class also provides helper methods that build on the capabilities of the then() method, including:

  • otherwise() - shorthand for adding an onRejected callback;
  • always() - adds an onCompleted callback, similar to finally in a try..catch..finally block;
  • done() - terminates a Promise chain, rethrowing any unhandled rejections as Errors;
  • cancel() - a specialized rejection initiated from the Promise (consumer) rather than the Deferred (producer); and
  • log() - logs the resolution or rejection of a Promise, with an optional category and identifier.

Deferred

A Deferred is typically used within the body of a function that performs an asynchronous operation. When that operation succeeds, the Deferred should be resolved; if that operation fails, the Deferred should be rejected.

A Deferred is resolved by calling its resolve() method with an optional value, and is rejected by calling its reject() method with an optional reason. Once a Deferred has been fulfilled or rejected, it is considered to be complete and subsequent calls to resolve() or reject() are ignored.

Deferreds are the mechanism used to create new Promises. A Deferred has a single associated Promise that can be safely returned to external consumers to ensure they do not interfere with the resolution or rejection of the deferred operation.

Resolver

Resolvers are used internally by Deferreds to create, resolve and reject Promises, and to propagate fulfillment and rejection.

Developers never directly interact with a Resolver.

Each Deferred has an associated Resolver, and each Resolver has an associated Promise. A Deferred delegates resolve() and reject() calls to its Resolver's resolve() and reject() methods. A Promise delegates then() calls to its Resolver's then() method. In this way, access to Resolver operations are divided between producer (Deferred) and consumer (Promise) roles.

When a Resolver's resolve() method is called, it fulfills with the optionally specified value. If resolve() is called with a then-able (i.e. a Function or Object with a then() function, such as another Promise) it assimilates the then-able's result; the Resolver provides its own resolve() and reject() methods as the onFulfilled or onRejected arguments in a call to that then-able's then() function. If an error is thrown while calling the then-able's then() function (prior to any call back to the specified resolve() or reject() methods), the Resolver rejects with that error. If a Resolver's resolve() method is called with its own Promise, it rejects with a TypeError.

When a Resolver's reject() method is called, it rejects with the optionally specified reason.

Each time a Resolver's then() method is called, it captures a pair of optional onFulfilled and onRejected callbacks and returns a Promise of the Resolver's future value as transformed by those callbacks.

Consequence

Consequences are used internally by Resolvers to capture and notify callbacks, and propagate their transformed results as fulfillment or rejection.

Developers never directly interact with a Consequence.

A Consequence forms a chain between two Resolvers, where the result of the first Resolver is transformed by the corresponding callback before being applied to the second Resolver.

Each time a Resolver's then() method is called, it creates a new Consequence that will be triggered once its originating Resolver has been fulfilled or rejected. A Consequence captures a pair of optional onFulfilled and onRejected callbacks.

Each Consequence has its own Resolver (which in turn has a Promise) that is resolved or rejected when the Consequence is triggered. When a Consequence is triggered by its originating Resolver, it calls the corresponding callback and propagates the transformed result to its own Resolver; resolved with the callback return value or rejected with any error thrown by the callback.

API

Creating, Resolving and Rejecting Promises

Create a deferred:

import com.codecatalyst.promise.Deferred;

...

var deferred:Deferred = new Deferred();

Resolve that deferred:

deferred.resolve( value );

Or, reject that deferred:

deferred.reject( reason );

Obtain the promise linked to that deferred to pass to external consumers:

import com.codecatalyst.promise.Promise;

...

var promise:Promise = deferred.promise;

Adding Callbacks, Chaining and Transformation

Add (optional) onFullfilled and onRejected callbacks to that promise:

promise.then( onFulfilled, onRejected );

Those callbacks can subsequently transform the value that was fulfilled or the reason that was rejected. Each call to then() returns a new Promise of that transformed value; i.e., a Promise that is fulfilled with the callback return value or rejected with any error thrown by the callback.

Or, add an onRejected callback using the shorthand method:

promise.otherwise( onRejected );

which is equivalent to:

promise.then( null, onRejected );

Because then() returns a new Promise, these statements can be chained:

promise
    .then( parseData )
    .then( populateUI )
    .otherwise( recover );

Add an onCompleted callback that is executed regardless of whether the Promise is resolved or rejected:

promise
    .then( parseData )
    .then( populateUI )
    .otherwise( recover )
    .always( cleanup );

always() is similar to finally in a try..catch..finally block.

Cancellation

Cancellation is a specialized form of rejection, one which can be initiated from the Promise (consumer) rather than the Deferred (producer). If a Promise is still pending, calling its cancel() method triggers a rejection with a CancellationError that will propagate to any Promises originating from that Promise.

NOTE: Cancellation only propagates to Promises that branch from the target Promise. It does not traverse back up to parent branches, as this would reject nodes from which other Promises may have branched, causing unintended side-effects.

Unhandled Rejections

One of the pitfalls of interacting with Promise-based APIs is the tendency for important errors to be silently swallowed unless an explicit rejection callback is specified.

For example:

promise
    .then( function () {
        // logic in your callback throws an error and it is interpreted as a rejection.
        throw new Error( 'Boom!' );
    });

// The error is silently swallowed.

This problem can be addressed by terminating the Promise chain with the done() method:

promise
    .then( function () {
        // logic in your callback throws an error and it is interpreted as a rejection.
        throw new Error( 'Boom!' );
    })
    .done();

// The error is thrown on the next tick of the event loop.

The done() method ensures that any unhandled rejections are rethrown as Errors.

NOTE: The done() method should only be used by a consumer of a Promise-returning API at the terminating node of a Promise chain. A Promise-returning API should never use done() internally for a Promise it intends to return to a consumer; otherwise, that Promise's rejection will be thrown as an RTE and will not actually be propagated to consumers.

Logging

The log() method provides a simple way to programmatically introduce logging within a Promise chain.

var category:String = "DataLoader";

dataService
    .load( id )
    .log( category )
    .then( parseData )
    .log( category, "Data parsing" )
    .then( populateUI )
    .log( category, "UI population" );

The optional category and identifier are intended to be incorporated into the resulting log entry.

Ex.

[DEBUG] DataLoader: Promise resolved with value: <value>
[DEBUG] DataLoader: Data parsing resolved with value: <value>
[ERROR] DataLoader: UI population rejected with reason: <reason>

The Promise class can be configured with a custom logger function with the following function signature:

function log( category:String, level:int, message:String, ...parameters ):void {
    // ...
}

Custom logger functions can be used to integrate with logging frameworks.

promise-as3 includes to example custom logger functions:

TraceLogger is a custom logger function that logs messages via trace().

To register this logger:

import com.codecatalyst.promise.logger.TraceLogger;

...

// This only needs to be done once within your application.
Promise.registerLogger(TraceLogger.log);

FlexLogger is a custom logger function that logs messages via Flex's mx.logging.Log.

import com.codecatalyst.promise.logger.FlexLogger;

...

// This only needs to be done once within your application.
Promise.registerLogger(FlexLogger.log);

Multiple custom logger functions can be registered. Loggers can be unregistered using the unregisterLogger() method:

Promise.unregisterLogger(TraceLogger.log);
// or
Promise.unregisterLogger(FlexLogger.log);

Adapting Immediate Values, Promises and AsyncTokens

Immediate values, foreign Promises (i.e. a Promise from another Promises/A implementation), and AsyncTokens can be adapted using the Promise.when() helper method.

To adapt an immediate value:

var promise:Promise = Promise.when( 123 );

To adapt a foreign Promise:

var promise:Promise = Promise.when( foreignPromise );

To adapt an AsyncToken:

import com.codecatalyst.promise.adapters.AsyncTokenAdapter;

...

// NOTE: Only need to do this once for the entire application.
Promise.registerAdapter( AsyncTokenAdapter.adapt );

var token:AsyncToken = ...

var promise:Promise = Promise.when( token );

Reference and Reading

Acknowledgements

License

Copyright (c) 2013 CodeCatalyst, LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.