JavaScript之Promise对象

来自:开源中国
时间:2019-08-25
阅读:
Promise.all()

Promise.all 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.all([p1, p2]);

上面代码中,Promise.all 方法接受一个数组作为参数,p1、p2 都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。(Promise.all方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。)

p的状态由p1、p2决定,分成两种情况。

  • 只有 p1、p2 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2 的返回值组成一个数组,传递给 p 的回调函数。

  • 只要 p1、p2 之中有一个被 rejected,p 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。

示例:

试想一个页面聊天系统,我们需要从两个不同的 URL 分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现。

// 并行执行异步任务
var p1 = new Promise(function (resolve, reject) {
    setTimeout(function() {
        // 模拟请求,请求状态为200代表成功,不是200代表失败
        if (status === 200) {
            resolve('P1');
        } else {
            reject('error');
        }
    }, 500);
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 输出:['P1', 'P2']
}).catch(function(error) {
    console.log(error); // 如果p1执行失败,则输出:error
});

注意,如果作为参数的 Promise 实例,自己定义了 catch 方法,那么它一旦被 rejected,并不会触发 Promise.all() 的 catch 方法。

Promise.race()

Promise.race 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

var p = Promise.race([p1, p2]);

上面代码中,只要 p1、p2 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

Promise.race 方法的参数与 Promise.all 方法一样,如果不是 Promise 实例,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理。

示例:

有些时候,多个异步任务是为了容错。比如,同时向两个 URL 读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现。

// 多任务容错
var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 400, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P2'
});
Promise.resolve()

有时需要将现有对象转为 Promise 对象,Promise.resolve 方法就起到这个作用。

Promise.resolve方法的参数分成四种情况:

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

(2)参数是一个 thenable 对象

thenable 对象指的是具有 then 方法的对象,比如下面这个对象。

var thenable = {
    then: function (resolve, reject) {
        resolve(42);
    }
};

Promise.resolve 方法会将这个对象转为 Promise 对象,然后就立即执行 thenable 对象的 then 方法。

var thenable = {
    then: function (resolve, reject) {
        resolve(42);
    }
};

var p1 = Promise.resolve(thenable);
p1.then(function (value) {
    console.log(value);  // 42
});

上面代码中,thenable 对象的 then 方法执行后,对象 p1 的状态就变为 resolved,从而立即执行最后那个 then 方法指定的回调函数,输出 42。

(3)参数不是具有 then 方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有 then 方法的对象,则 Promise.resolve 方法返回一个新的 Promise 对象,状态为 resolved。

var p = Promise.resolve('Hello');

p.then(function (s) {
    console.log(s)
});
// 'Hello'

var p1 = Promise.resolve(true);

p1.then(function (b) {
    console.log(b)
});
// true

var p2 = Promise.resolve(1);

p1.then(function (n) {
    console.log(n)
});
// 1

(4)不带有任何参数

Promise.resolve 方法允许调用时不带参数,直接返回一个 resolved 状态的 Promise 对象。

所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用 Promise.resolve 方法。

Promise.reject() Promise.reject 方法也会返回一个新的 Promise 实例,该实例的状态为 rejected。

注意,Promise.reject 方法的参数,会原封不动地作为 reject 的参数,变成后续方法的参数。这一点与 Promise.resolve 方法不一致。

var thenable = {
    then(resolve, reject) {
        reject('出错了');
    }
};

Promise.reject(thenable)
    .catch(e = > {
    console.log(e === thenable)
})
// true

上面代码中,Promise.reject 方法的参数是一个 thenable 对象,执行以后,后面 catch 方法的参数不是 reject 抛出的 出错了 这个字符串,而是 thenable 对象。

加载图片

我们可以将图片的加载写成一个 Promise,一旦加载完成,Promise 的状态就发生变化。

function (path) {
    return new Promise(function (resolve, reject) {
        const image = new Image();
        image.onload = resolve;
        image.onerror = reject;
        image.src = path;
    });
};

封装ajax

我们可以将 ajax 请求写成一个 Promise,根据请求的不同状态改变 Promise 的状态。

function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                } //欢迎加入全栈开发交流圈一起学习交流:864305860 
            }    //面向1-3年前端人员
        };      //帮助突破技术瓶颈,提升思维能力  
        request.open(method, url);
        request.send(data);
    });
}

总结 优点:

可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数(回调地狱)。 在异步执行的流程中,可以把执行代码和处理结果的代码清晰地分离开来。

缺点:

无法取消 Promise,一旦新建它就会立即执行,无法中途取消。 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

返回顶部
顶部