Promise 对象

Promise 对象用于处理异步请求,保存一个异步操作最终完成(或失败)的结果

语法

new Promise(
    /* executor */
    function(resolve, reject) {...}
);

/*
   来自谷歌翻译
      promise:承诺,约定
      resolve:解决,决定,下决心
      reject:拒绝,驳回,抵制 
*/

参数:

promise 构造函数接受一个 executor 函数作为参数,该函数的两个参数分别是 resolve 和 reject,它们是两个函数(executor 函数 在 Promise 构造函数返回新对象之前被调用)。

resolve 函数被调用时,将 promise 对象从 “未完成” 变为 “成功” (即 pending –> fulfilled)

reject 函数被调用时,将 promise 对象从 “未完成” 变为 “失败” (即 pending –> rejected)

描述

promise 对象是一个代理对象(代理一个值),被代理的值在 promise 对象创建时可能是未知的。它允许你为异步操作的成功和失败分别绑定相应的处理方法,使得异步方法可以像同步方法那样返回值,但不是立即返回最终执行结果,而是一个能代表未来出现的结果的 promise 对象。

上面提到过,一个 promise 对象有三个状态:

• pending:初始状态,不是成功或失败状态

• fulfilled:成功完成

• rejected:失败

pending 状态可能触发 fulfilled 状态并传递一个值给相应的状态处理方法,也可能触发 rejected 状态并传递失败信息。当其中任一种情况发生时,promise 对象的 then 方法绑定的处理方法就会被调用。

(then 方法包含两个参数:onfulfilled 和 onrejected,也都是函数。当 promise 对象的状态为 fulfilled 时调用,调用 then 的 onfulfilled 方法;反之,调用 onrejected 方法。所以异步操作的完成和绑定处理方法之间不存在竞争)

then 方法的使用语法:

promise.then(function(value) {
  // onfulfilled
}, function(error) {   // 第二个参数是可选的,不一定要提供
  // onrejected 
});

示例:

    let myFirstPromise = new Promise(function(resolve, reject){
        // 当异步代码执行成功时,调用resolve()方法;失败时,调用reject()方法
        // 此处,使用定时器来模拟异步代码,实际编码可能是XHR请求或HTML5的一些API方法
        setTimeout(function(){
            //resolve('成功!')  //代码执行成功,调用resolve()方法
            resolve('成功!')
        }, 2000)
    })
    
    myFirstPromise.then(function(successMessage){
        // successMessage 是上面调用resolve()方法传入的值
        // successMessage 参数不一定非要是字符串类型,这里只是举个例子
        console.log('Yay!'+successMessage)
    })

    console.log('看看我的位置在哪里?')

运行结果:

想要某个函数拥有 promise 功能,只需要让其返回一个 promise 即可

    function timeout (ms) {
        return new Promise ((resolve, reject) => {
            setTimeout(resolve, ms, 'done')
            /* 
                setTimeout(function, milliseconds, param1, param2, ...)
                function 必需,
                milliseconds 可选,默认为0。执行或调用function需要等待的时间
                param1,param2 可选,传给执行函数的其它参数(IE9- 不支持该参数)
            */
        })
    }

    timeout(2000).then((value) => {
        console.log(value)
    })

    console.log('立即出现……')

上面代码中,timeout 方法返回一个 Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(参数ms)以后,Promise 实例状态变成 fulfilled,就会触发 then方法的onfulfilled 方法。运行结果如下:

Promise 新建后就会立即执行

    let promise = new Promise((resolve, reject) => {
        console.log('立即执行 Promise !')
        resolve()
    })

    promise.then(() => {
        console.log('fulfilled')
    })

    console.log('我什么时候出现呢?')

上面代码中,Promise 新建后立即执行,所以最先输出的是 “立即执行 Promise !”。then 方法指定的回调函数将在当前脚本所有同步任务执行完以后才会执行,所以 “fulfilled” 最后输出。运行结果如下:

下面是一个用 Promise 对象实现异步加载图像的例子



    
    
    
    Document




   function imgLoad(url) {
       // 使用 Promise() 构造函数创建 promise 实例, 接受两个参数,resolve 和 reject 函数
       return new Promise ( (resolve, reject) => {
           /******************** 异步加载图片的标准方法 ************************/
           // 创建 XMLHTTPRequest 对象
           var xhr = new XMLHttpRequest()
           /* 
              open(method,url,async) 规定请求的类型、URL以及是否异步处理请求
              method:请求的类型,GET 或 POST
              url:文件在服务器上的位置
              async:true --> 异步,false ---> 同步
           */
           xhr.open('GET', url)
           /*
               BLOB (binary large object),表示二进制大对象
               在实际Web应用中,Blob更多是图片二进制形式的上传与下载,虽然其可以实现几乎任意文件的二进制传输
               responseType设为blob,表示服务器传回的是二进制对象
           */
           xhr.responseType = 'blob'
           // 处理请求时,检查是否成功
           xhr.onload = function () {
               if (xhr.status === 200) {
                   // 如果成功的话,把响应内容作为参数传给resolve 方法
                    resolve(xhr.response)
               } else {
                   // 如果失败的话,把服务器返回的错误状态码(status ->响应的 HTTP 状态,statusText ->HTTP 状态的说明,如 200 对应的statusText就是 OK)作为错误信息展示出来
                   reject(Error('Image didn't load successfully; error code:' + xhr.statusText))
               }
           }
           // 检测错误
           xhr.onerror = function () {
               // 假如一开始就全部请求失败,有可能是网络错误
               reject(Error('There was a network error.'))
           }
           /*
              send(string)   将请求发送到服务器
              string:仅用于 POST 请求
           */
           xhr.send()
       })
   }
   // 获取body,然后创建一个新的 image 对象
   var body = document.querySelector('body'),
       myImg = new Image();
   // 调用 imgLoad 方法并传入我们想获取的 url,在 imgLoad 方法后面链式调用 then 方法
   imgLoad('Penguins.jpg').then( (response) => {
       // 创建一个新的 URL 对象
        var imageURL = window.URL.createObjectURL(response)
        myImg.src = imageURL
        body.appendChild(myImg)
   }, (Error) => {
       console.log(Error)
   })



用Apache服务器打开效果:(成功)

双击网页打开效果:(失败)

如果调用 resolve 函数和 reject 函数时带有参数,那么,它们的参数会被传递给回调函数。reject 函数的参数通常是 Error 对象的实例,表示抛出的错误;resolve 函数的参数除了是正常值以外,还可以是另一个 Promise,如:

var p1 = new Promise(function (resolve, reject) {
  // ...
});

var p2 = new Promise(function (resolve, reject) {
  // ...
  resolve(p1);
})

上例中,p1的状态决定了p2的状态,如果p1的状态是 pending,那么,p2的回调函数就会等待p1的状态改变;如果p1的状态已经是 fulfilled 或 rejected,那么,p2的回调函数就会立即执行

     var p1 = new Promise ( (resolve, reject) => {
        setTimeout( () => reject(new Error('fail')), 3000)
    })

    var p2 = new Promise ( (resolve, reject) => {
        setTimeout( () => resolve(p1), 1000)
    })

    p2.then( result => console.log(result))
      .catch( error => console.log(error))
    
    // Error: fail

上面代码中,p1是一个 Promise,3s后变成一个 rejected。p2的状态在 1s后改变,resolve 方法返回的是 p1。由于 p2 返回的是另一个 Promise,导致p2自己的状态无效了,由 p1的状态决定p2的状态,所以后面的 then语句都变成了针对 p1。又过了2秒,p1变为rejected,导致触发catch方法指定的回调函数。

注意,调用resolve或reject并不会终结 Promise 的参数函数的执行

    new Promise((resolve, reject) => {
        resolve(1);
        console.log(2);
    }).then(r => {
        console.log(r);
    });
    //2
    //1

上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务

下例展示了 Promise 的一些机制。 testPromise() 方法在每次点击

Promise 的值的填充过程都被日志记录(logged)下来,这些日志信息展示了方法中的同步代码和异步代码是如何通过Promise完成解耦的。



    
    
    
    Document
    


    
    
let promiseCount = 0; function testPromise () { let thisPromiseCount = ++promiseCount, log = document.getElementById('log'); /* insertAdjacentHTML() 将指定的文本解析为HTML或XML,并将结果节点插入到DOM树中的指定位置。它不会重新解析它正在使用的元素, 因此它不会破坏元素内的现有元素。这避免了额外的序列化步骤,使其比直接innerHTML操作更快。 语法:element.insertAdjacentHTML(position, text) position是相对于元素的位置,'beforebegin' -> 元素自身的前面 text是要被解析为HTML或XML,并插入到DOM树中的字符串 */ log.insertAdjacentHTML('beforeend', thisPromiseCount+ ') Started(Sync code started)
') // 创建一个 Promise 实例,用于计数,从1开始 let p1 = new Promise( (resolve, reject) => { log.insertAdjacentHTML('beforeend', thisPromiseCount+ ') Promised started(Async code started)
') // 用定时器模拟异步代码 window.setTimeout(function (){ // 状态从 pending 变成 fulfilled resolve(thisPromiseCount) }, Math.random()*2000+1000 ) }) p1.then(function (val) { // 记录 fulfillment 的值 log.insertAdjacentHTML('beforeend', val+ ') Promise fulfilled(Async code started)
') }).catch( // 当 promise 状态变成rejected,调用catch 方法 (reason) => { // 记录 rejection 的原因 console.log('Handle rejected promise ('+reason+') here.') } ) log.insertAdjacentHTML('beforeend', thisPromiseCount + ') Promise made (Sync code terminated)
') }

快速点击按钮多次可以观察到Promises填充值的过程,运行结果如下:

博客园-原创精华区稿源:博客园-原创精华区 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 前端开发 » Promise 对象

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录