promise的使用

栏目: JavaScript · 发布时间: 6年前

内容简介:promise的使用

本文主要介绍一下 promise 在一些场景下的使用,同时还有对 Promises/A+ 规范的一点个人理解。原本是想总体的讲一下JavaScript异步编程的内容,后由于内容很多,能力有限,目前计划分成几部分。

Promise A+ 规范

为了更好的表述,我把 Promise A+ 规范拆成了两部分介绍:

第一部分规范:

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

    promise的使用

  2. 一个 Promise 必须提供一个 then 方法以访问其当前值、终值和据因。

    promise.then(onFulfilled, onRejected);
    

第二部分规范:

  1. Promise then 方法需要 return 一个新的 Promise 出来,如下:

    promise2 = promise1.then(onFulfilled, onRejected);
    
  2. 如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值;如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。

  3. 如果 promise 本身状态变更到 Fulfilled 之后,返回调用 onFulfilledonRejected 的解析值 x ,与新的 promise2 进行 promise 的解析过程 [[Resolve]](promise2, x)x 的取值不同,有不同的情况:

  4. x 为一个 promise ,则使 promise2 接受 x 的状态:

    var promise2 = new Promise(function(resolve, reject){
    
        resolve();
    }).then(function(data){
        // 对应于 x 的返回值
        return new Promise(function(resolve, reject){
            resolve('x = promise');
        });
    });
    
    promise2.then(function(data){
        // 打印 x = promise
        console.log(data);
    });
    

    为了看的清晰一点,我把链式拆开了,正常是这样子:

    new Promise(function(resolve, reject){
    
        resolve();
    }).then(function(data){
        // 对应于 x 的返回值
        return new Promise(function(resolve, reject){
            resolve('x = promise');
        });
    }).then(function(data){
        // 打印 x = promise
        console.log(data);
    });
    
  5. x 为一个对象或者函数,如果有 then 方法,将会执行 then 方法, then 方法 this 指向 x 本身,如下:

    new Promise(function(resolve, reject){
    
        resolve();
    }).then(function(data){
        // 对应于x的返回值
        return {
              a: 1,
              then: function(resolve, reject){
                    // 打印 1
                    console.log(this.a);
                    resolve({a: 2});
              }
        };
    }).then(function(data){
        // 打印 2
       console.log(data.a);
    });
    
  6. 如果 x 没有 then 方法,那么, x 将会做为值来 满足 promise2 ,如下:

    new Promise(function(resolve, reject){
    
        resolve();
    }).then(function(data){
        // 对应于x的返回值
        return {
              a: 1
        };
    }).then(function(data){
        // 打印 1
       console.log(data.a);
    });
    

简单实现的 Promise 对象

这是一个简单实现的 Promise 对象,根据我在上面自己分出来的第一部分规范,实现了『有状态』和 then 方法。

function Promise(fn){
    var state = 'pending';
    var value;
    var deferred;

    function resolve(newValue){
        value = newValue;
        state = 'resolved';

        if(deferred) {
            handle(deferred);
        }
    }

    function handle(onResolved){
        if(state === 'pending') {
            deferred = onResolved;
            return;
        }

        onResolved(value);
    }

    this.then = function(onResolved){
        handle(onResolved);
    };

    fn(resolve);
}

new Promise 的时候:

  1. 执行 fn1 并把 resolve 方法交给 fn1 准备调用
  2. 同步执行 then 方法,将 fn2 作为参数调用 handle 方法
    1. 如果 resolve 同步执行,那么 state 变更为 resolved ,紧接着 onResolved 也立即执行
    2. 如果 resolve 没有同步执行,那么 state 依旧是 pending ,那么将 onResolved 的引用保存起来,等到 resolve 异步执行的时候,再调用 onResolved 方法
new Promise(function fn1(resolve){

    setTimeout(function(){
        resolve('eventual value');
    }, 1000);
}).then(function fn2(data){
    // 等待 1000ms 后,打印 eventual value
    console.log(data);
});

接下来根据我在上面自己分出来的第二部分规范,下面的实现增加了链式和穿透的特性。

function Promise(fn){
    var state = 'pending';
    var value;
    var deferred = null;

    function resolve(newValue){
        // 这部分是新增的,见注一
        if(newValue && typeof newValue.then === 'function') {
            newValue.then(resolve);
            return;
        }
        value = newValue;
        state = 'resolved';

        if(deferred) {
            handle(deferred);
        }
    }

    function handle(handler){
        if(state === 'pending') {
            deferred = handler;
            return;
        }
        // 这部分是新增的,见注二
        if(!handler.onResolved) {
            handler.resolve(value);
            return;
        }

        var ret = handler.onResolved(value);
        handler.resolve(ret);
    }

    this.then = function(onResolved){
        return new Promise(function(resolve){
            handle({
                onResolved: onResolved,
                resolve: resolve
            });
        });
    };

    fn(resolve);
}

由于 then() 永远返回一个新的 Promise 对象,导致每次都至少有一个 promise 对象被创建、解决然后被忽略,这就产生了一定程度了内存浪费。

then 方法返回新的 Promise 对象,这个 promise2resolve 的值是 promise1 的返回值。 handle() 函数的最后两行体现了这一点, handler 对象保存了 onResolved() 回调函数和 resolve() 函数的引用。在链式调用中保存了多个 resolve() 函数的拷贝,每一个 promise 对象的内部都拥有一个自己的 resolve() 方法,并在闭包中运行。 这建立起了第一个 promise 与第二个 promise 之间联系的桥梁。

new Promise(function(resolve){

    setTimeout(function(){
        resolve('promise1 eventual value');
    }, 1000);
}).then(function(data){
    // 等待 1000ms 后,打印 promise1 eventual value
    console.log(data);

    return {
        then: function(resolve){
            setTimeout(function(){
                resolve('promise2 eventual value');
            }, 1000);
        }
    }
}).then(function(data){
    // 等待 2000ms 后,打印 promise2 eventual value
    console.log(data);
});

注一: then 方法内部的返回值可以是 Promise 对象, 如果是要通过 then 方法获取终值,对应第二部分规范第4和5条。

注二: new Promise.then().then(function (data) {}) 里面第一个 then 会被跳过, promise 也会用上一个 promise 返回的终值来传递,对应第二部分规范第2条。

new Promise(function(resolve){

    setTimeout(function(){
        resolve('promise1 eventual value');
    }, 1000);
}).then().then(function(data){
    // 等待 1000ms 后,打印 promise1 eventual value
    console.log(data);
});

上面的代码一直忽略掉一个问题,错误处理,这里不做引述,原因是我觉得实现方式类似,需要注意的点会在下面讲。

Promise 使用

Promise.all()

当我们想使用 forEach() 的时候,我们可以使用 Promise.all()Promise.all() 以一个 promise 对象组成的数组为输入,返回另一个 promise 对象。这个对象的状态只会在数组中所有的 promise 对象的状态都变为 resolved 的时候才会变成 resolved 。可以将其理解为异步的 for 循环。注意的是,如果输入的一系列 promise 对象中,有一个的状态变为 rejected ,那么 all() 返回的 promise 对象的状态也会变为 rejected

var array = [];
for (var i = 0; i < 4; i++) {

    (function(){
        var a = i;
        array.push(new Promise(function(resolve){

            setTimeout(function(){
                resolve(a);
            }, a * 1000);
        }));
    })(i);
}

new Promise(function(resolve){

    resolve();
}).then(function(){

    return Promise.all(array);
}).then(function(data){
    // 等待 3000ms 后, 打印 [ 0, 1, 2, 3 ]
    console.log(data);
});

Promise.resolve()

可以将同步代码包装成 promise 形式,后面可以加 .then() 或者 .catch() ,我觉得在封装接口的时候很适用。

new Promise(function(resolve, reject){
    resolve(someSynchronousValue);
}).then(...);

精简为:

Promise.resolve(someSynchronousValue).then(...);

cacth() 和 then(null, …) 并不完全相同

下面两个代码片段是等价的:

somePromise().catch(function(err){
    // handle error
});

somePromise().then(null, function(err){
    // handle error
});

但是,这并不意味着下面的两个代码片段是等价的

somePromise().then(function(){
    return someOtherPromise();
}).catch(function(err){
    // handle error
});

somePromise().then(function(){
    return someOtherPromise();
}, function(err){
    // handle error
});

结论就是,当使用 then(resolveHandler, rejectHandler)rejectHandler 不会捕获在 resolveHandler 中抛出的错误。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

旷世之战――IBM深蓝夺冠之路

旷世之战――IBM深蓝夺冠之路

纽伯 / 邵谦谦 / 清华大学出版社 / 2004-5 / 35.0

本书作者Monty Neworn是国际计算机象棋协公的主席,作者是用生动活泼的笔触描写了深蓝与卡斯帕罗夫之战这一引起全世界关注的历史事件的前前后后。由于作者的特殊身份和多年来对计算机象棋的关心,使他掌握了许多局外人不能得到的资料,记叙了很多鲜为人知的故事。全书行文流畅、文笔优美,对于棋局的描述更是跌宕起伏、险象环生,让读者好像又一次亲身经历了那场流动人心的战争。 本书作为一本科普读物......一起来看看 《旷世之战――IBM深蓝夺冠之路》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具