ES6 Promise对象

本文深入讲解Promise对象的概念、特点及基本用法,包括状态管理、异步操作处理、错误捕捉和链式调用技巧。同时,探讨Promise.all、Promise.race、Promise.resolve和Promise.reject的高级应用,以及在实际场景如图片加载、数据获取和流程控制中的实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作

 都可以用同样的方法进行处理。

 1.Promise特点:

1.1

 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

 1.2

 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。

只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。

 2.基本用法

 Promise对象是一个构造函数,用来生成Promise实例

{

let promise = new Promise(function (resolve, reject) {

// if (/* 异步操作成功 */) {

// resolve(value);

// } else {

// reject(error);

// }

})

}

 Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

{

let promise = new Promise(function (resolve, reject) {

// resolve('women');

reject('cuowu');

}).then(function (value) {

console.log(value)

}, function (value) {

console.log(value)

});

 promise.then(function (value) {

 // success

 console.log(value)

 }, function (error) {

 // failure

 });

}
{

function timeout(ms) {

return new Promise((resolve, rejected) => {

setTimeout(resolve, ms, 'done');

})

}

timeout(1000).then((value) => {

console.log(value)

})

}

 Promise 新建后就会立即执行。

{

let promise = new Promise(function (resolve, rejected) {

console.log('Promise');

resolve();

});

promise.then(function () {

console.log('resolved.');

});

console.log('Hi!');

}

 上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,

 将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

 异步加载图片

{

function loadImageanysc(url) {

return new Promise(function (resolve, rejected) {

let image = new Image();

image.onload = function () {

resolve(url);

}

image.onerror = function () {

rejected(url)

}

image.src = url;

// console.log(imag.src)

}).then(function (url) {

console.log(url + '图片加载出来了')

},

function (name) {

console.log(url + '图片加载不出来')

}

)

}

loadImageanysc('https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2310514390,3580363630&fm=27&gp=0.jpg')

}
{

const getJSON = function (url) {

const promise = new Promise(function (resolve, reject) {

const handler = function () {

if (this.readyState !== 4) {

return;

}

if (this.status === 200) {

resolve(this.response);

} else {

reject(new Error(this.statusText));

}

};

const client = new XMLHttpRequest();

client.open("GET", url);

client.onreadystatechange = handler;

client.responseType = "json";

client.setRequestHeader("Accept", "application/json");

client.send();

});

return promise;

};

getJSON("/posts.json").then(function (json) {

console.log('Contents: ' + json);

}, function (error) {

console.error('出错了', error);

});

}

 getJSON是对 XMLHttpRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个Promise对象。

 需要注意的是,在getJSON内部,resolve函数和reject函数调用时,都带有参数。

{

const p1 = new Promise(function (resolve, reject) {

// ...

});



const p2 = new Promise(function (resolve, reject) {

// ...

resolve(p1);

})

}

 上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。

{

const p1 = new Promise(function (resolve, reject) {

setTimeout(() => reject(new Error('fail')), 3000)

})

const p2 = new Promise(function (resolve, reject) {

setTimeout(() => resolve(p1), 1000)

})

p2.then(result => console.log(result))

.catch(error => console.log(error))

}

 Error: fail

上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。

 由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。

 又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

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

 3.Promise.prototype.then()

P romise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

 采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作), 这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

{

getJSON("/post/1.json").then(function (post) {

return getJSON(post.commentURL);

}).then(function funcA(comments) {

console.log("resolved: ", comments);

}, function funcB(err) {

console.log("rejected: ", err);

});

}

 使用箭头函数更加简介

{

getJSON('/post/1.json').then(post => {

return getJSON(post.commentUrl);

}).then(comments => {

console.log("resolved: ", comments);

}, err => {

console.log("rejected: ", err)

})

}

 4.Promise.prototype.catch()

 Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

{

const promise = new Promise(function (resolve, reject) {

throw new Error('test');

});

promise.catch(function (error) {

console.log(error);

});

// Error: test

}

 上面代码中,promise抛出一个错误,就被catch方法指定的回调函数捕获。注意,上面的写法与下面两种写法是等价的。

 写法一

{

const promise = new Promise(function (resolve, reject) {

try {

throw new Error('test');

} catch (e) {

reject(e);

}

});

promise.catch(function (error) {

console.log(error);

});

}

 写法二

{

const promise = new Promise(function (resolve, reject) {

reject(new Error('test'));

});

promise.catch(function (error) {

console.log(error);

});

}

比较上面两种写法,可以发现reject方法的作用,等同于抛出错误。

 如果 Promise 状态已经变成resolved,再抛出错误是无效的。

{

const promise = new Promise(function (resolve, reject) {

resolve('ok');

throw new Error('test');

});

promise

.then(function (value) { console.log(value) })

.catch(function (error) { console.log(error) });

// ok

}

一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

{

// bad

promise

.then(function (data) {

// success

}, function (err) {

// error

});



// good

promise

.then(function (data) { //cb

// success

})

.catch(function (err) {

// error

});

}

 上面代码中,第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,

 也更接近同步的写法(try/catch)。因此,建议总是使用catch方法,而不使用then方法的第二个参数。

 5.romise.prototype.finally()

 finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

{

function getPromise() {

return new Promise((resolve, reject) => {

// debugger

resolve();

}).then(() => {

console.log('成功')

}).catch(() => {

console.log('失败或错误')

}).finally(() => {

console.log('最终结果')

})

}

getPromise();

}

 服务器使用 Promise 处理请求,然后使用finally方法关掉服务器。

{

server.listen(port)

.then(function () {

// ...

})

.finally(server.stop);

}

 finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,

 finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

 finally本质上是then方法的特例。

{

function getPromise() {

return new Promise((resolve, reject) => {

// debugger

resolve();

})

}

getPromise().finally(() => {

});

//等同于

getPromise().then(

result => {

// 语句

return result;

},

error => {

// 语句

throw error;

})

}

 从下面的实现还可以看到,finally方法总是会返回原来的值。

{

// resolve 的值是 undefined

Promise.resolve(2).then(() => { }, () => { })



// resolve 的值是 2

Promise.resolve(2).finally(() => { })



// reject 的值是 undefined

Promise.reject(3).then(() => { }, () => { })



// reject 的值是 3

Promise.reject(3).finally(() => { })

}

 6.Promise.all()

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

{

let p = Promise.all([p1, p2, p3])

}

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

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

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

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

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

{

const p1 = new Promise((resolve, reject) => {

resolve('hello');

})

.then(result => result)

.catch(e => e);



const p2 = new Promise((resolve, reject) => {

throw new Error('报错了');

})

.then(result => result)

.catch(e => e);



Promise.all([p1, p2])

.then(result => console.log(result))

}

 如果p2没有自己的catch方法,就会调用Promise.all()的catch方法。

 7.Promise.race()

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

{

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

}

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

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

 8.Promise.resolve()

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

{

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

}

 上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。

Promise.resolve等价于下面的写法。

{

Promise.resolve('foo')

// 等价于

new Promise(resolve => resolve('foo'))

}

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

 (1)参数是一个 Promise 实例

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

(2)参数是一个thenable对象

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

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

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

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

 (4)不带有任何参数

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

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

 9.Promise.reject()

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

 10.应用

 10.1加载图片

 10.2 Generator 函数和Promise的结合

 使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个Promise对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值