Promise实用理解

1.Promise含义

Promise 是异步编程的一种解决方案优于传统的解决方案——回调函数和事件。 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。 Promise对象代表一个异步操作,有三种状态:pending(进行中)、resolved(已成功)和rejected(已失败)。

2.Promise执行顺序

setTimeout(() => {
    console.log('a');
}, 0);
let p = new Promise((resolve, reject) => {
    console.log('b');
    resolve();
});
p.then(() => {
    console.log('d');
});
console.log('c');

// 控制台输出:
// 'b'
// 'c'
// 'd'
// 'a'

复制代码

要理解该输出顺序首先应该了解js的执行任务队列优先级(由高到低)

  • 主线程
  • Micro-task队列 (微任务)
  • Macro-tasks队列 (宏任务)

首先setTimeout属于宏任务扔进Macro-tasks队列,新建实例Promise时接受一个回调函数作为参数,注意此时该回调函数属于主线程会立刻执行,输出'b'紧接着执行resolve也就意味着该promise对象的状态将从pending更新为resolved,其挂载的回调函数也就是then里面的参数函数并不会立即执行,因为它属于微任务,所以丢进Micro-task队列。接下来输出'c',到目前为止主线程任务已经结束,接着执行微任务输出'd',最后执行宏任务输出'a'

3.Promise状态更新

let p1 = new Promise(function (resolve, reject) { 
    resolve('p1');
});
let p2 = new Promise(function (resolve, reject) {
    setTimeout(() => {
        resolve('p2')
    }, 100);
});
let p3 = new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject('p3')
        resolve('p3')
    }, 100);
});

p1.then((value) => {
    console.log(value);
})
p2.then((value) => {
    console.log(value);
})
p3.then((value) => {
    console.log('success', value);
}, (value) => {
    console.log('error', value);
})
console.log('p1:', p1);
console.log('p2:', p2);
console.log('p3:', p3);

setTimeout(() => {
    console.log('p1:', p1);
    console.log('p2:', p2);
    console.log('p3:', p3);
}, 100);

// 控制台输出
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[pending]]}
// p3: Promise {[[pending]]}
// p1
// p2
// error p3
// p1: Promise {[[resolved]]: "p1"}
// p2: Promise {[[resolved]]: "p2"}
// p3: Promise {[[rejected]]: "p3"}
复制代码

p1最新创建就调用了resolve则它的状态立刻变为resolved,值为p1,但此时p2和p3都为pending状态,100毫秒后p2输出值p2且状态转为resolved。 p3首先调用了reject则其状态转为rejected,值为p3,尽管下一行又调用了resolve但并没有任何作用忽略成功的回调,只有error p3。 这段实验也显示出Promise的一个特点

  • 调用then方法传入回调可以从外部接受promise的异步返回数据value,当嵌套多级异步操作时这种优势更大。
  • 状态的不可逆性,Promise的状态和值确定下来,后续再调用resolve或reject方法,不能改变它的状态和值。

3.Promise之then实例方法

new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('a');
    }, 1000);
}).then(function (value) {               
    console.log("第一个" + value);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(value + 'b');
        }, 1000);
    })
}).then(function (value) {              
    console.log("第二个" + value);
}).then(function (value) {
    console.log("第三个" + value);
    console.log(a);
}).then(function (value) {              
    console.log("第四个" + value);
}, (err) => {
    console.log("第四个error", err);
})

// 第一个a
// 第二个ab
// 第三个undefined
// 第四个error ReferenceError: a is not defined
复制代码

then方法是Promise的实例方法,调用then后的返回值依然是一个promise对象,注意它是全新的promise对象,一般可以看到then的链式调用,这里需要注意区别于jQuery的链式调用。jQuery是返回调用对象本身。当链式调用时要注意不能被它绕晕了,要抓住一个重点,我们只是在调用then方法而已,给它传参只是定义函数,并没有执行!什么时候执行?是根据你的异步操作后的promise状态如何更新以及何时更新而确定。 传给then的回调函数中的返回值影响着最终返回出的promise对象,参数的返回值一般有三种情况。

  • 一个普通的同步值,或者没写返回值默认就是undefined,当然它也属于普通同步值。则then最终返回的是状态是resolve成功的Promise对象,如上段代码的第三个输出,它的前一个then方法内部没有返回值则默认undefined,接下来就直接走进第三个then方法,且值value就是undefined
  • 返回新的Promise对象,then方法将根据这个Promise的状态和值创建一个新的Promise对象返回。如第二个输出,会等待上个then方法返回的新Promise对象状态的更新来确定,且会等待它的更新以及将最后的值传过来,这种情况也是当有多级异步操作所使用的方式。
  • throw一个同步异常,then方法将返回一个rejected状态的Promise, 值是该异常。如第四个输出!

4.Promise之catch实例方法

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

let p = new Promise((resolve, reject) => {
    //
});
p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));
复制代码

catch方法,它首先是捕捉处理错误,不论是promise调用了reject方法还是直接抛出错误,都会走到catch方法内进行处理。接下来就和then方法一样,返回的也是一个全新的Promise对象,错误处理的回调函数返回值同样有三种情况,具体看上个then方法。

let p = new Promise((resolve, reject) => {
    reject('失败')
});
p.then((val) => console.log('1then: success', val))
 .then((val) => console.log('2then: success', val))
 .catch((val) => console.log('3catch: error', val))
 .catch((val) => console.log('4catch: error', val))
 .then((val) => console.log('5then: success', val))

// 控制台输出

// 3catch: error 失败
// 5then: success undefined
复制代码

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。 上段代码首先p这个Promise对象(状态是resolved)遇到第一个then会忽略掉它定义的成功回调,注意此时调用完第一个then方法后的返回值是全新的Promise对象!且状态同样是resolved,为何会这样?因为它把p的状态进行了一层包装也就作为了自己的状态,且值也和它一样!所以说Promise的状态具有传递性。

因为这个错误目前并没有被捕获处理,所以继续向后传递。同样遇到第二个then时我们可以当做跳过它,但发生的细节和第一个then同理,直到3catch将这个错误捕获,所以输出3catch: error 失败。上面也提到catch也就是then的一个别名而已,本质其实差不多。故此时catch调用后的返回值再次是一个全新的promise对象,那状态呢?因为这边给catch传递的参数并没有定义返回值,所以默认就是一个同步值undefined,则catch返回的promise对象的状态就是resolved。那么它调用最后一个then输出5then: success undefined,也就不难理解了。

5.Promise之resolve、reject静态方法

let p1 = Promise.resolve('p1');
p1.then(val => console.log('success', val), val => console.log('error', val))

let p2 = Promise.reject('p2');
p2.then(val => console.log('success', val), val => console.log('error', val))
复制代码

当传入参数是一般同步值时则返回一个状态为resolve或reject的Promise对象,值也就是传入的参数,相应的会调用成功或失败的回调。

let p1 = Promise.resolve(1);
let p2 = Promise.resolve(p1);
let p3 = new Promise(function (resolve, reject) {
    resolve(p1);
});

console.log(p1 === p2)
console.log(p1 === p3)

p1.then((value) => { console.log('p1=' + value)})
p2.then((value) => { console.log('p2=' + value)})
p3.then((value) => { console.log('p3=' + value)})

// 控制台输出:
// true
// false
// p1=1
// p2=1
// p3=1
复制代码

当传入一个Promise对象时,则resolve就直接返回该Promise对象,故p1 === p2true,p3则为全新的Promise对象,但是它状态立刻变为resolve且值为p1,它会获取p1的状态和值作为自己的值。故p3=1

6.Promise之all、race静态方法

function timeout(who) {
    return new Promise(function (resolve, reject) {
        let wait = Math.ceil(Math.random() * 3) * 1000;
        setTimeout(function () {
            if (Math.random() > 0.5) {
                resolve(who + ' inner success');
            }
            else {
                reject(who + ' inner error');
            }
        }, wait);
        console.log(who, 'wait:', wait);
    });
}

let p1 = timeout('p1');
let p2 = timeout('p2');

p1.then((success) => { console.log(success) }).catch((error) => { console.log(error) })
p2.then((success) => { console.log(success) }).catch((error) => { console.log(error) })

// race只要有一个状态改变那就立即触发且决定整体状态失败还是成功.
// all只要有一个失败那就立即触发整体失败了,两个都成功整体才成功.
Promise.all([p1, p2])
    .then((...args) => {
        console.log('all success', args)
    })
    .catch((...args) => {
        console.log('someone error', args)
    })

// 控制台输出(情况1)
// p1 wait: 3000
// p2 wait: 1000p2 inner error
// someone error [ 'p2 inner error' ]
// p1 inner success

// 控制台输出(情况2)
// p1 wait: 2000
// p2 wait: 2000
// p1 inner success
// p2 inner success
// all success [ [ 'p1 inner success', 'p2 inner success' ] ]

复制代码

all、race方法接受数组作为参数,且数组每个成员都为Promise对象。如果不是的话就调用Promise.resolve方法,将其转为 Promise 实例,再进一步处理。使用表示要包装的多个promise异步操作来确定。具体可以看代码理解,要多动手自己试验!

如有错误或疑问欢迎指正留言:

参考文献:

阮一峰ECMAScript 6 入门 Promise

八段代码彻底掌握 Promise

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Promise是一种用于处理异步操作的JavaScript对象。它可以用于各种环境和情况下,以简洁和可读性高的方式处理异步任务。 在浏览器环境中,Promise常用于处理网络请求。我们可以使用XHR对象发送异步请求,并在其回调函数中创建Promise实例来处理请求的结果。在接收到响应后,Promise可以根据请求的成功或失败状态,执行相应的回调函数或捕获错误信息。 而在Node.js环境中,Promise也非常常见。Node.js中的许多操作都是异步的,比如读取文件、数据库查询等。使用Promise可以更方便地处理这些异步操作,并进行错误处理和结果传递。我们可以通过调用内置模块中的函数,创建Promise实例,然后使用Promise的方法链式调用来处理异步操作。 Promise还可以在前端框架(如React、Vue),以及后端框架(如Express、Koa)中使用。在前端框架中,Promise常用于管理数据的获取和页面渲染的顺序。在后端框架中,Promise常用于处理中间件和路由,以及数据库操作等异步任务。 总而言之,Promise可以在各种环境中使用。它的优势在于提供了一种更清晰、更可读的方法来处理异步任务。无论是浏览器、Node.js、前端框架还是后端框架,都可以方便地使用Promise来管理和处理异步操作。 ### 回答2: Promise 是一种在 JavaScript 中用于处理异步操作的解决方案。它可以让我们更方便地编写和组织代码,使得处理异步操作变得更加直观和可读。 Promise 可以用于任何需要进行异步操作的环境中,包括前端和后端开发。在前端开发中,常见的使用场景是处理网络请求。例如,在发送 AJAX 请求获取数据时,我们可以使用 Promise 对象来处理异步操作,以便在数据返回后进行相应的处理。Promise 还可以用于处理其他需要异步操作的场景,比如处理文件读取、定时器函数等。 在后端开发中,Promise 也广泛应用于处理数据库查询、文件读取、接口调用等需要异步操作的场景。使用 Promise 可以使异步代码更加易读和可维护,并且可以通过链式调用的方式更加方便地处理多个异步操作之间的依赖关系。 另外,Promise 不仅可以在浏览器环境和 Node.js 等服务器端环境中使用,还可以在各种支持 JavaScript 的平台中使用,比如移动端开发、桌面端应用程序等。 总结来说,Promise 可以在任何需要处理异步操作的环境中使用。无论是前端还是后端开发,它都能为我们提供一种统一的、简洁的方式来处理异步操作,提高代码的可读性和可维护性。 ### 回答3: promise的使用环境主要是在异步编程中。在传统的JavaScript中,使用回调函数来处理异步操作,但由于回调函数存在嵌套、多层嵌套的情况,导致代码的可读性和可维护性差。而promise则能够通过链式调用的方式,更加清晰和优雅地处理异步操作。 promise在多种场景中都能得到广泛应用。首先,在前端开发中,当进行异步请求时,比如通过XMLHttpRequest或fetch获取数据信息,可以使用promise来进行处理。promise可以将异步操作包装成一个Promise对象,通过.then()方法来处理成功的回调和.catch()方法来处理失败的回调,使得代码更加简洁和易读。 其次,在多个异步操作有依赖关系的情况下,promise也能起到很大的作用。通过使用promise的.all()方法,可以将多个promise对象组合成一个新的promise对象,等待所有的异步操作都完成后再执行相应的操作。这在需要等待多个异步请求返回结果后再进行下一步操作时,十分方便和有效。 此外,promise还可以搭配async/await语法一起使用,以提供更加直观和简洁的异步编程方式。使用async修饰符来声明一个异步函数,在函数内部使用await关键字来等待promise的返回值,使得代码逻辑更加清晰和易于理解。 总之,promise是一种在异步编程中非常强大和实用的工具,它能够优化代码结构,改善代码的可读性和可维护性,提供了更加优雅和简洁的处理异步操作的方式。由于其广泛的应用场景和强大的功能,promise已经成为现代JavaScript开发中不可或缺的工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值