ES6(2015)Promise

Posted 优小U

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6(2015)Promise相关的知识,希望对你有一定的参考价值。

1. 异步操作

JS是单线程

单线程,即同一个时间只能处理一个任务。

为什么 JS 是单线程的?作为浏览器脚本语言,javascript 的主要用途是与用户互动,以及操作 DOM 。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定 JavaScript同时有两个线程,一个线程在某个 DOM 节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

单线程就意味着,所有任务都需要排队,前一个任务结束,才能执行后一个任务。如果前一个任务耗时很长,那么后一个任务就不得不一直等待,于是乎,JS 设计者们把所有任分成两类,同步和异步。

  • 同步:只有前一个任务执行完毕,才能执行后一个任务
  • 异步:当同步任务执行到某个 WebAPI 时,就会触发异步操作,此时浏览器会单独开线程去处理这些异步任务。
console.log(1)
setTimeout(() => { // 异步任务,放入任务队列中
    console.log(2)
}, 0)
console.log(3)
// 1 3 2

步任务和异步任务的执行过程:
在这里插入图片描述
回调地狱

ajax('static/a.json', res => {
    console.log(res)
    ajax('static/b.json', res => {
        console.log(res)
        ajax('static/c.json', res => {
            console.log(res)
        })
    })
})

如果嵌套变多,代码层次就会变深,维护难度也随之增加。

这就被称为 “回调地狱” 或者“回调深渊”。

2. Promise 基本语法

Promise 就是为了解决“回调地狱”问题的,它可以将异步操作的处理变得很优雅。

// 创建Promise实例
const promise = new Promise(function(resolve, reject) {
    // ... some code
    if ( /* 异步操作成功 */ ) {
        resolve(value)
    } else {
        reject(error)
    }
})

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

  • 处理结果正常的话,调用resolve(处理结果值),将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
  • 处理结果错误的话,调用reject(Error对象),将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise.then(function(value) {
    // success
}, function(error) {
    // failure
})

Promise 内部是有状态的(pending、fulfilled、rejected),Promise 对象根据状态来确定执行哪个方法。Promise 在实例化的时候状态是默认 pending 的,当异步操作是完成的,状态会被修改为 fulfilled,如果异步操作遇到异常,状态会被修改为 rejected,可以通过下图来看下状态的走向:

在这里插入图片描述

状态转化是单向的,不可逆转,已经确定的状态(fulfilled/rejected)无法转回初始状态(pending),而且只能是从 pending 到 fulfilled 或者 rejected

3. Promise.prototype.then()

var promise = new Promise(function(resolve, reject) {
    resolve('传递给then的值')
})
promise.then(function(value) {
    console.log(value)
}, function(error) {
    console.error(error)
})

这段代码创建一个 Promise 对象,定义了处理 onFulfilled 和 onRejected 的函数(handler),然后返回这个 Promise 对象;当执行resolve时,会进入then方法的成功回调方法。then方法的第一个方法为成功回调,第二个方法为失败的回调,参数为resolve/reject传递的参数。

4. Promise.prototype.catch()

使用 Promise 对象的 catch 方法来捕获异步操作过程中出现的任何异常。

function test() {
    return new Promise((resolve, reject) => {
        reject(new Error('es'))
    })
}

test().catch((e) => {
    console.log(e.message) // es
})

reject 不会触发catch,new Error会触发catch,不建议在 Promise 内部使用 throw 来触发异常,而是使用 reject(new Error()) 的方式来做,因为 throw 的方式并没有改变 Pronise 的状态

5. Promise.resolve()

一般情况下我们都会使用 new Promise() 来创建 Promise 对象,但是除此之外我们也可以使用其他方法。

在这里,我们将会学习如何使用 Promise.resolvePromise.reject 这两个静态方法。

new Promise(function(resolve) {
    resolve(42)
})

Promise.resolve(42).then(function(value) {
    console.log(value)
})

Promise.resolve 作为 new Promise() 的快捷方式,在进行 Promise 对象的初始化或者编写测试代码的时候都非常方便。

6. Promise.reject()

Promise.reject(error) 是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。

new Promise(function(resolve, reject) {
    reject(new Error('出错了'))
})

Promise.reject(new Error('Error!'))

7. Promise.all()

Promise.all 生成并返回一个新的 Promise 对象,所以它可以使用 Promise 实例的所有方法。参数传递promise数组中所有的 Promise 对象都变为resolve的时候,该方法才会返回, 新创建的 Promise 则会使用这些 promise 的值。
如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返回一个reject的新的 Promise 对象。

var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.all([p1, p2, p3]).then(function(results) {
    console.log(results) // [1, 2, 3]
})

8. Promise.race()

Promise.race 生成并返回一个新的 Promise 对象。参数 promise 数组中的任何一个 Promise 对象如果变为 resolve 或者 reject 的话, 该函数就会返回,并使用这个 Promise 对象的值进行 resolve 或者 reject。

var p1 = Promise.resolve(1)
var p2 = Promise.resolve(2)
var p3 = Promise.resolve(3)
Promise.race([p1, p2, p3]).then(function(value) {
    console.log(value) // 1
})

以上是关于ES6(2015)Promise的主要内容,如果未能解决你的问题,请参考以下文章

[转]JS - Promise使用详解2(ES6中的Promise)

ES6 Promise 先拉出来遛遛

Promise学习

在 ES6 中扩展 Promise

8.大白话讲解Promise

VSCode自定义代码片段12——JavaScript的Promise对象