zoukankan      html  css  js  c++  java
  • Promises/A+规范

    为什么需要异步编程方式

    一个函数执行之后,在它后面顺序编写的代码中,如果能够直接使用它的返回结果或者它修改之后的引用参数,那么我们通常认为该函数是同步的。

    如果一个函数的执行结果或者其修改的引用参数,需要通过设置回调函数或者回调事件的方式来获取,而在其后顺序编写的代码中无法直接获取的话,那么我们通常认为这样的函数是异步的。

    阻塞带来的问题是当前线程(或进程)会陷入等待,一直等到阻塞结束,这样就会造成线程(或进程)资源的浪费。所以通常认为阻塞是不够高效的。

    如果要编写非阻塞代码,使用同步方式会变得有些复杂,且不够灵活。同步方式的非阻塞代码通常会使用 select 模式,例如 curl_multi_select、stream_select、socket_select等就是PHP中提供的一些典型的 select 模式的函数。我们说它复杂且不够灵活,是因为例如使用上面的 select 模式编写同步的非阻塞代码时我们需要先构造一个并发任务的列表,之后手动构造循环来执行这些并发的任务,在循环开始之后,虽然这几个任务可以并发,但是这个循环相对于其后的代码总体上仍然是阻塞的,我们要想拿到这些并发任务的结果时,仍然需要等待。select 虽然可以同时等待多个任务中某一个或几个就位后,再执行后续操作,但仍然有一部分时间是被等待消耗掉的。而且如果是纯同步非阻塞的情况下,我们也很难在循环开始后,动态添加更多的任务到这个循环中去。所以希望程序能够更加高效、更加灵活,就需要引入异步方式

    传统的异步方式有什么问题

    回调是最简单直接的异步模式,只要在调用异步函数时设置一个或多个回调函数,函数就会在完成时自动调用回调函数。如果你的程序逻辑够简单,简单的一两层回调也许并不会让你觉得异步方式的编程有什么麻烦。但如果你的程序逻辑一旦有些复杂,你可能就会被层层回调搞得疲惫不堪了。当然,实际上你的程序需要层层回调的原因,也许并不是你的程序逻辑真的复杂,而是你没有办法将回调函数中的参数结果传出来,所以你就不得不将另一个回调函数传进去

    为什么要引入Promise

    Promise要解决的问题是,如何将回调方法的参数从回调方法中传递出来,让它可以像同步函数的返回结果一样,在回调函数以外的控制范围内,可以传递和复用。

    Promise是抽象异步处理对象以及对其进行各种操作的组件

    Promises/A+规范

    • 解决(fulfill):指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但是在后世的 promise 实现多以 resolve 来取代之。
    • 拒绝(reject):指一个 promise 失败时进行的一系列操作。
    • 终值(eventual value):所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
    • 拒因(reason):也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。

    Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 Promise 的终值或本 Promise 不能执行的原因。

    术语:

    • Promise:它是一个拥有 then 方法的对象或函数,其行为符合本规范;
    • thenable:是一个定义了 then 方法的对象或函数,即拥有 then 方法;
    • 值(value):指任何 JavaScript 的合法值(包括 undefined、thenable 和 promise);
    • 异常(exception):是使用 throw 语句抛出的一个值;
    • 拒因(reason):表示一个 promise 的拒绝原因;

    要求:

    一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。

    等待态(Pending)

    处于等待态时,promise 需要满足以下条件:

    • 可以迁移至执行态或拒绝态

    执行态(Fulfilled)

    处于执行态时,promise 需要满足以下条件:

    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的终值

    拒绝态(Rejected)

    处于拒绝态时,promise 需满足以下条件:

    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的拒因

    这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(指当 value 或 reason 不是基本值时,只要求其引用地址相等,但属性值可被更改)

    Then 方法

    一个 promise 必须提供一个 then 方法以访问其当前值、终值和拒因。

    promise 的 then 方法接受两个参数:

    promise.then(onFulfilled, onRejected)

    参数可选

    onFulfilled 和 onRejected 都是可选参数。

    • 如果 onFulfilled  不是函数,其必须被忽略
    • 如果 onRejected 不是函数,其必须被忽略

    onFulfilled 特性

    如果 onFulfilled 是函数:

    • 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值
    • 在 promise 执行结束前其不可被调用
    • 其调用次数不可超过一次

    onRejected 特性

    如果onRejected 是函数:

    • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的拒因
    • 在 promise 被拒绝执行前其不可被调用
    • 其调用次数不可超过一次

    调用时机

    onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用(平台代码:指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。这个事件队列可以采用"宏任务macrotask"机制或"微任务microtask"机制来实现。由于 promise 的实施代码本身就是平台代码(即都是JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。)

    macrotask 和 microtask 两个概念,这表示异步任务的两种分类。在挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,首先在 macrotask 的队列(这个队列也被叫做 task queue)中取出第一个任务,执行完毕后取出 microtask 队列中的所有任务顺序执行;之后再取 macrotask 任务,周而复始,直至两个队列的任务都取完。

    两个类别的具体分类如下:

    • macro-task:script(整体代码),setTimeout,setInterval,setImmediate,I/O,UI rendering
    • micro-task:process.nextTick、Promises(浏览器原生的Promises)、Object.observe、MutationObserver

    调用要求

    onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)

    多次调用

    then 方法可以被同一个 promise 调用多次

    • 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
    • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

    返回

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

    promise2 = promise1.then(onFulfilled, onRejected);
    

      

    • 如果 onFulfilled 或者 onRejected 返回一个值 x,则运行下面的 Promise 解决过程:[ [ Resolve ] ] (promise2,x)
    • 如果 onFulfilled 或者 onRejected 抛出一个异常 e,则 promise2 必须拒绝执行,并返回拒因 e
    • 如果 onFulfilled 不是函数且 promise1 成功执行,promise2 必须成功执行并返回相同的值
    • 如果 onRejected 不是函数且 promise1 拒绝执行,promise2 必须拒绝执行并返回相同的拒因

    不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。

    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。

  • 相关阅读:
    剑指offer-二维数组中的查找
    TF-IDF(term frequency–inverse document frequency)
    Java实现中文字符串的排序功能
    当前课程
    【R】资源整理
    CentOS相关
    【转】Setting up SDL Extension Libraries on MinGW
    【转】Setting up SDL Extension Libraries on Visual Studio 2010 Ultimate
    【转】Setting up SDL Extension Libraries on Code::Blocks 12.11
    【转】Setting up SDL Extension Libraries on Visual Studio 2019 Community
  • 原文地址:https://www.cnblogs.com/xi-jie/p/11972254.html
Copyright © 2011-2022 走看看