`
xiangzhihong
  • 浏览: 9273 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Promises机制

 
阅读更多

Javascript 采用回调函数(callback)来处理异步编程。从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验。于是便有了 CommonJS 的 Promises/A 规范,用于解决回调金字塔问题。

回调金字塔

那么何为回调金字塔呢?简单的讲就是回调里面嵌套回调。例如:

asyncOperation(function(data){
  // 处理 `data`
  anotherAsync(function(data2){
      // 处理 `data2`
      yetAnotherAsync(function(){
          // 完成
      });
  });
})

Pomise简介

那么什么是Promise机制呢?Promise代表一个目前还不可用,但是在未来的某个时间点可以被解析的值,它允许以一种同步的方式来编写异步代码。例如,使用Promise API执行异步调用远程服务,但是在发起请求前你并不知道返回的数据对象是什么样子,你可以创建一个Promise对象作为未来某个时间返回的数据对象,在此期间, Promise对象扮演了真实数据的代理角色。 
例如,使用Promise进行网络请求。

this.AsyncFunction(para).then(
            (para)=>{
             // 处理成功的事件
            }
        ).catch(
            (error)=>{
             // 处理失败的事件
            } )

Promises 将嵌套的 callback ,改造成一系列的.then的连缀调用,去除了层层缩进的糟糕代码风格。 Promises 不是一种解决具体问题的算法,而已一种更好的代码组织模式。接受新的组织模式同时,也逐渐以全新的视角来理解异步调用。

Then 方法

一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected)

onFulfilled 和 onRejected 都是可选参数。如果 onFulfilled和onRejected 不是函数,其必须被忽略。onFulfilled和onRejected必须在执行后才能被调用,并且只能调用一次。

then 方法可以被同一个 promise 调用多次。当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调;当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调。

then 方法必须返回一个 promise 对象 。即:

promise2 = promise1.then(onFulfilled, onRejected);   
  • 1

如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)。

Promise 解决过程

Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。

这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

运行 [[Resolve]](promise, x) 需遵循以下步骤:

x 与 promise 相等

如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise

x 为 Promise

如果 x 为 Promise ,则使 promise 接受 x 的状态 :

  • 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
  • 如果 x 处于执行态,用相同的值执行 promise
  • 如果 x 处于拒绝态,用相同的据因拒绝 promise x 为对象或函数

如果 x 为对象或者函数:

把 x.then 赋值给 then

  • 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
  • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise 
    ,第二个参数叫做 rejectPromise:
  • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
  • 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
  • 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用 
    如果调用 then 方法抛出了异常 e:
  • 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之 否则以 e 为据因拒绝 promise
  • 如果 then 不是函数,以 x 为参数执行 promise
  • 如果 x 不为对象或者函数,以 x 为参数执行 promise

如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise

Promises规范

到目前为止Promises指定了A、B、D、A+…版本。

Promises /A

promise 表示一个最终值,该值由操作完成时返回。 
promise 有三种状态:未完成 (unfulfilled),完成 (fulfilled) 和失败 (failed)。 
promise 的状态只能由未完成转换成完成,或者未完成转换成失败 。 
promise 的状态转换只发生一次。 
promise 有一个 then 方法, then 方法可以接受 3 个函数作为参数。前两个函数对应 promise 的两种状态 fulfilled 和 rejected 的回调函数。第三个函数用于处理进度信息(对进度回调的支持是可选的)。

promiseSomething().then(function(fulfilled){
        //当 promise 状态变成 fulfilled 时,调用此函数
    },function(rejected){
        //当 promise 状态变成 rejected 时,调用此函数
    },function(progress){
        //当返回进度信息时,调用此函数
    });

Promises/B

在 Promises/A 的基础上, Promises/B 定义了一组 promise 模块需要实现的 API:

when(value, callback, errback_opt)

如果 value 不是一个 promise ,那么下一事件循环 callback 会被调用, value 作为 callback 的传入值。如果 value 是一个 promise , promise 的状态已经完成或者变成完成时,那么下一事件循环 callback 会被调用, resolve 的值会被传入 callback ; promise 的状态已经失败或者变成失败时,那么下一事件循环 errback 会被调用, reason 会作为失败的理由传入 errback 。

asap(value, callback, errback_opt)
  • 1

与 when 最大的区别,如果 value 不是一个 promise ,会被立即执行,不会等到下一事件循环。

enqueue(task Function)
  • 1

尽可能快地在接下来的事件循环调用 task 方法。

get(object, name)
  • 1
  • 2

返回一个获得对象属性的 promise 。

post(object, name, args)
  • 1

返回一个调用对象方法的 promise 。

put(object, name, value)
  • 1

返回一个修改对象属性的 promise 。

del(object, name)

返回一个删除对象属性的 promise 。

makePromise(descriptor Object, fallback Function)

返回一个 promise 对象,该对象必须是一个可调用的函数,也可能是可被实例化的构造函数。

综合示例:

 {
        "when": function(errback){...},
        "get": function(name){...},
        "put": function(name, value){...},
        "post": function(name, args){...},
        "del": function(name){...},
    }
defer()

返回一个对象,该对象包含一个 resolve(value) 方法和一个 promise 属性。 
当 resolve(value) 方法被第一次调用时, promise 属性的状态变成 完成,所有之前或之后观察该 promise 的 promise 的状态都被转变成 完成。 value 参数如果不是一个 promise ,会被包装成一个 promise 的 ref 。 resolve 方法会忽略之后的所有调用。

reject(reason String)
  • 1

返回一个被标记为 失败 的 promise 。 
一个失败的 promise 上被调用 when(message) 方法时,会采用如下两种方法之一

  1. 如果存在 errback , errback 会以 reason 作为参数被调用。 when 方法会将 errback 的返回值返回。
  2. 如果不存在 errback , when 方法返回一个新的 reject 状态的 promise 对象,以同一 reason 作为参数。
ref(value)

如果 value 是 promise 对象,返回 value 本身。否则,返回一个 resolved 的 promise ,携带如下 handle 。

  1. when(errback),忽略 errback ,返回 resolved 值
  2. get(name),返回 resolved 值的对应属性。
  3. put(name, value) ,设置 resolved 值的对应属性。
  4. del(name),删除 resolved 值的对应属性。
  5. post(name, args), 调用 resolved 值的对应方法。
  6. 其他所有的调用都返回一个 reject ,并携带 “Promise does not handle NAME” 的理由。
isPromise(value) Boolean
  • 1

判断一个对象是否是 promise。

method(name String)

获得一个返回 name 对应方法的 promise 。返回值是 “get”, “put”, “del” 和 “post” 对应的方法,但是会在下一事件循环返回。

Promises/D

为了增加不同 promise 实现之间的可互操作性, Promises/D 规范对 promise 对象和 Promises/B 规范做了进一步的约定。以达到鸭子类型的效果( Duck-type Promise )。简单来说 Promises/D 规范,做了两件事情:

  1. 如何判断一个对象是 Promise 类型。
  2. 对 Promises/B 规范进行细节补充。

判断Promise 对象

Promise 对象必须是实现 promiseSend 方法。

  1. 在 promise 库上下文中,如果对象包含 promiseSend 方法就可以甄别为 promise 对象
  2. promiseSend 方法必须接受一个操作名称,作为第一个参数 操作名称是一个可扩展的集合,下面是一些保留名称:

    when,此时第三个参数必须是 rejection 回调。 
    rejection 回调必须接受一个 rejection 原因(可以是任何值)作为第一个参数 
    get,此时第三个参数为属性名(字符串类型) 
    put,此时第三个参数为属性名(字符串类型),第四个参数为新属性值。 
    del,此时第三个参数为属性名 
    post,此时第三个参数为方法的属性名,接下来的变参为方法的调用参数 
    isDef

  3. promiseSend方法的第二个参数为 resolver 方法
  4. promiseSend方法可能接受变参
  5. promiseSend方法必须返回undefined

Promises/A+

前面提到的 Promises/A/B/D 规范都是有 CommonJS 组织提出的, Promises/A+是有一个自称为Promises/A+ 组织发布的,该规范是以 Promises/A 作为基础进行补充和修订,旨在提高 promise 实现之间的可互操作性。

Promises/A+ 对.then方法进行细致的补充,定义了细致的Promise Resolution Procedure流程,并且将.then方法作为 promise 的对象甄别方法。此外, Promises/A+ 还提供了兼容性测试工具,以确定各个实现的兼容性。

Promise判断

状态机

var PENDING = 0;
var FULFILLED = 1;
var REJECTED = 2;

function Promise() {
  // store state which can be PENDING, FULFILLED or REJECTED
  var state = PENDING;

  // store value or error once FULFILLED or REJECTED
  var value = null;

  // store sucess & failure handlers attached by calling .then or .done
  var handlers = [];
}

状态变迁 
仅支持两种状态变迁, fulfill 和 reject。

function Promise() {
    // ...

  function fulfill(result) {
    state = FULFILLED;
    value = result;
  }

  function reject(error) {
    state = REJECTED;
    value = error;
  }

}

fulfill 和 reject 方法较为底层,通常更高级的 resolve 方法开放给外部。

function Promise() {

  // ...

  function resolve(result) {
    try {
      var then = getThen(result);
      if (then) {
        doResolve(then.bind(result), resolve, reject)
        return
      }
      fulfill(result);
    } catch (e) {
      reject(e);
    }
  }
}

resolve 方法可以接受一个普通值或者另一个 promise 作为参数,如果接受一个 promise 作为参数,等待其完成。 promise 不允许被另一个 promise fulfill ,所以需要开放 resolve 方法。 resolve 方法依赖一些帮助方法定义如下:

function getThen(value) {
  var t = typeof value;
  if (value && (t === 'object' || t === 'function')) {
    var then = value.then;
    if (typeof then === 'function') {
      return then;
    }
  }
  return null;
}


function doResolve(fn, onFulfilled, onRejected) {
  var done = false;
  try {
    fn(function (value) {
      if (done) return
      done = true
      onFulfilled(value)
    }, function (reason) {
      if (done) return
      done = true
      onRejected(reason)
    })
  } catch (ex) {
    if (done) return
    done = true
    onRejected(ex)
  }
}

这里 resolve 和 doResolve 之间的递归很巧妙,用来处理 promise 的层层嵌套( promise 的 value 是一个 promise )。

构造器

function Promise(fn) {
    // ...
    doResolve(fn, resolve, reject);
}

.done 方法

function Promise(fn) {
  // ...

  function handle(handler) {
    if (state === PENDING) {
      handlers.push(handler);
    } else {
      if (state === FULFILLED &&
        typeof handler.onFulfilled === 'function') {
        handler.onFulfilled(value);
      }
      if (state === REJECTED &&
        typeof handler.onRejected === 'function') {
        handler.onRejected(value);
      }
    }
  }

  this.done = function (onFulfilled, onRejected) {
    // ensure we are always asynchronous
    setTimeout(function () {
      handle({
        onFulfilled: onFulfilled,
        onRejected: onRejected
      });
    }, 0);
  }
  // ...
}

.then 方法

function Promise(fn) {
    // ...
    this.then = function (onFulfilled, onRejected) {
      var self = this;
      return new Promise(function (resolve, reject) {
        return self.done(function (result) {
          if (typeof onFulfilled === 'function') {
            try {
              return resolve(onFulfilled(result));
            } catch (ex) {
              return reject(ex);
            }
          } else {
            return resolve(result);
          }
        }, function (error) {
          if (typeof onRejected === 'function') {
            try {
              return resolve(onRejected(error));
            } catch (ex) {
              return reject(ex);
            }
          } else {
            return reject(error);
          }
        });
      });
    }
    // ...
}

小结

jQuery 1.8 之前的版本, jQuery 的 then 方法只是一种可以同时调用 done 、 fail 和 progress 这三种回调的速写方法,而 Promises/A 规范的 then 在行为上更像是 jQuery 的 pipe 。 jQuery 1.8 修正了这个问题,使 then 成为 pipe 的同义词。不过,由于向后兼容的问题, jQuery 的 Promise 再如何对 Promises/A 示好也不太会招人待见。

此外,在 Promises/A 规范中,由 then 方法生成的 Promise 对象是已执行还是已拒绝,取决于由 then 方法调用的那个回调是返回值还是抛出错误。在 JQuery 的 Promise 对象的回调中抛出错误是个糟糕的主意,因为错误不会被捕获。 
最后一个例子揭示了,实现 Promise 的关键是实现好 doResolve 方法,在完事以后触发回调。而为了保证异步 setTimeout(fun, 0); 是关键一步。

附:Promise

分享到:
评论

相关推荐

    Scala.for.the.Impatient.2nd.Edition.2016.12

    对于并发编程,Scala内置的Actor模型和Futures/Promises机制使得编写并发程序变得简单而安全。书中会介绍如何使用这些工具来处理多线程问题,同时避免常见的并发陷阱。 最后,书中可能还会探讨Scala与其他Java平台...

    前端开源库-batch-promises

    "batch-promises" 是前端开发中一个实用的工具,它简化了批量处理异步任务的过程,提高了代码质量,并提供了更好的错误处理机制。开发者可以根据自己的需求,灵活地在项目中应用这个库,以提升整体的开发体验和应用...

    fastly-promises-源码.rar

    本文将深入探讨Fastly-Promises的源码,揭示其背后的机制和设计思想。 一、Promise基础概念 Promise是ES6引入的一种处理异步操作的对象,代表一个未来完成的操作,并且提供了一种统一的接口来管理这个操作的状态...

    前端开源库-async-promises

    "async-promises"是一个专门针对异步操作的开源库,它结合了经典的`async`库(https://github.com/caolan/async)的功能与现代JavaScript的Promise机制,以提供更高效、更简洁的异步控制流解决方案。 `async`库是...

    promises:PromisesA+ 规范的超级简单实现,适用于 lulz

    在JavaScript的世界里,Promise是一种处理异步操作的重要机制,它为复杂的异步代码提供了一种更...通过研究这个"Promises:A+规范的超级简单实现",开发者可以深入理解Promise的工作机制,从而更好地应用到实际项目中。

    整合服务

    5. **事件驱动编程**:JavaScript中的EventEmitter类和Promises机制可以实现异步事件处理,这在处理多个服务之间的异步交互时非常有效。 6. **数据转换和格式化**:在整合服务时,数据可能需要在不同格式间转换,...

    有关Promises异步问题详解

    Promises是JavaScript中用于处理异步操作的一种机制,它的出现是为了改善传统的回调地狱问题,使得异步编程更加清晰和易于管理。Promises的概念最初源于CommonJS小组提出的Promises/A规范,后来在ES6中被正式引入,...

    promises-源码.rar

    通过阅读源码,我们可以更好地理解Promise的工作机制,包括如何维护状态、如何触发回调以及如何处理异步执行。理解源码有助于我们在实际开发中更有效地使用Promise,避免常见问题。 总结,Promise作为JavaScript...

    promises

    在JavaScript编程中,Promises是处理异步操作的重要机制,它是ES6引入的一种新的语法特性。Promises的设计理念是解决回调地狱(Callback Hell)问题,提高代码的可读性和可维护性。本项目"promises"很可能是针对这一...

    Promises-Promises:DFW Area AngularJS Meetup 102015的示例代码

    Promise可以帮助我们更好地组织代码,避免回调地狱,并提供错误处理机制。 此外,AngularJS的$rootScope.$apply()和$rootScope.$digest()方法也与Promise紧密相关,它们用于检测并执行模型的改变。在异步操作后,...

    promises-promises

    总的来说,这个话题涉及了JavaScript中的Promise机制,如何通过创建、链式调用来处理异步操作,以及如何在实际项目中应用Promise来构建可读性更强、更易于维护的代码。学习和理解Promise对于任何JavaScript开发者来...

    promises:懒惰的承诺

    这里的“Promises:懒惰的承诺”可能是指Haskell中的一种实现异步操作的方式,它结合了函数式编程语言的特性,如延迟计算(lazy evaluation)和类型系统的优势。 Haskell 是一种纯函数式编程语言,它的主要特点之一...

    JavaScript中Promises/A+规范的实现

    在提供的链接http://www.cnblogs.com/strick/p/5092092.html中,可能详细介绍了Promise/A+规范的实现细节,包括Promise构造函数、then方法、错误处理机制等方面。同时,`promise.html`可能是展示Promise用法的一个...

    完整的承诺:微小的宏库,旨在简化Promises的ClojureScript测试

    为了更好地利用Unbroken Promises,你需要对ClojureScript的宏机制有深入的理解。宏在编译时运行,可以修改源代码,生成新的代码结构。在处理Promise时,宏可以插入必要的延迟和回调,以确保测试能够正确地等待异步...

    The Promises and Perils of Mining GitHub.zip

    2. **社区互动信息**:除了代码,GitHub 还记录了用户的贡献行为、问题讨论和项目维护等信息,这些社交网络数据有助于理解开发者的行为模式和协作机制。 3. **实时性与动态性**:由于 GitHub 的实时更新特性,可以...

    使用promises的“child_process”模块的简单包装器_JavaScript_下载.zip

    本项目“使用promises的“child_process”模块的简单包装器”正是为了解决这个问题,通过创建一个Promise包装器,使得调用`child_process`模块的API更加简洁、易于理解和管理。 首先,我们来理解`child_process`...

    pizzaGoGo-Promises:Javascript练习

    在JavaScript编程中,Promises是处理异步操作的重要机制,特别是在现代Web开发中,它扮演着不可或缺的角色。"pizzaGoGo-Promises"这个练习项目旨在帮助开发者加深对Promise的理解和应用,通过实际操作来提升...

    Javascript中的异步编程规范Promises/A详细介绍

    这种状态机制确保了异步操作的顺序性和避免了回调地狱。 Promises/A的核心在于`then`方法,它允许开发者注册对Promise状态变化的监听器。`then`接收三个参数:fulfilledHandler、errorHandler和progressHandler。...

Global site tag (gtag.js) - Google Analytics