一文读懂-Promise

8次阅读

共计 4770 个字符,预计需要花费 12 分钟才能阅读完成。

一文读懂 Promise

什么是 Promise

$.ajax({success: (res) => {
        $.ajax({success: (res) => {
                $.ajax({success: (res) => {//...}
                })
            }
        })
    }
})

这就是典型的回调地狱,不仅代码臃肿,可读性差,而且耦合度过高,不易维护。代码无法复用,还容易隐藏 bug。

Promise 规范的出现就是为了解决这种问题。

这里强调一下,Promise 是一种解决方案,它是一种规范。

ES6 原生提供了 Promise 对象,在日常开发中经常会接触到 Promise 相关操作,本文将介绍 Promise 的使用方法及相关原理,希望能有所帮助。

Promise 有三种状态:

  • Pending(等待态)
  • Fulfilled(成功态)
  • Rejected(失败态)

Promise 的特点是状态只能由 Pending 转换为 Fulfilled 或 Rejected,并且一旦改变不再更改。

new Promise((resolve,reject) => {/*executor*/})

Promise 是一个带有 resolve,reject 两个参数的构造函数 (executor)。这个构造函数在 Promise 创建的时候 立即执行 ,resolve 和 reject 两个函数在被调用时,分别将 Promise 的状态更改为 fulfilled(成功态)和 rejected(失败态),并传递成功的值或失败的原因。executor 内部通常会执行一些异步操作,当异步操作执行完毕时,可根据执行结果相应地调用 resolve 或 reject(可能成功可能失败)。如果 executor 抛出一个错误,那么该 Promise 也将转为失败态。

Promise 原型上还具有 then 和 catch 方法,调用后返回一个 Promise 实例,因此可以被链式调用。

每一个 Promise 实例都带有一个 then 方法,该方法有两个参数(onFulfilled,onRejected),分别为 Promise 成功和失败时的回调。

let p = new Promise((resolve,reject) => {resolve('success');
})
p.then(value => {console.log(value);        //success
}).then(value => {console.log(value);        // 没有返回值时,undefined
})

当返回了一个值或者一个成功态的 Promise 时,then 返回的 Promise 都是成功态,若没有返回值时也是成功态,而回调函数的参数值为undefind

当抛出一个错误或者返回一个失败态 Promise,then 返回的 Promise 都是失败态。

let p = new Promise((resolve,reject) => {reject('fail');
})
p.then(() => {},err => {console.log(err);        //fail
});

then 方法传递的成功 / 失败函数,这两个函数可以返回一个 promise;如果返回的是一个 promise,则该 promise 状态将作为下一次 then 的结果。

let p = new Promise((resolve,reject) => {resolve('success')
});
let p2 = new Promise((resolve,reject) => {resolve('success2')
})
p.then(value => {return p2;                //p2 的成功态将作为下一次 then 的状态}).then(value => {console.log(value)
})

Promise.prototype.catch方法返回也是一个 Promise,使用方式同 then 方法,用于处理异常捕获情况。该方法捕获属于就近捕获原则,离错误最近的一个 catch 将会捕获错误并进行处理,后续将不再捕获。

若失败状态被 then 方法中失败函数回调触发,则 catch 方法将不进行捕获。

let p1 = new Promise((resolve,reject) => {resolve('success');
});
p1.then(value => {throw 'a new error';}).catch(e => {console.log(e);        //a new error
}).then(() => {console.log('after a catch the chain is restored');
})

Promise.all方法返回一个新的 Promise 对象,在参数对象中所有的 promise 对象都成功时才会触发成功态,一旦有任意一个对象失败则立即触发该 Promise 对象的失败态。当触发成功态后,会把一个包含所有 Promise 返回值的数组 按顺序返回。如果触犯失败态,则会把第一个失败的 Promise 对象错误信息返回。

let p1 = new Promise((resolve,reject) => {setTimeout(() => {resolve('p1')
    },0)
});
let p2 = new Promise((resolve,reject) => {setTimeout(() => {resolve('p2')
    },0)
});
let promise = Promise.all([p1,p2]).then(value => {console.log(value);        //['p1','p2']
})

Promise.race 方法是把参数中任意第一个完成状态转换的 Promise 作为成功或失败状态,并返回该 Promise 对象。

let p1 = new Promise((resolve,reject) => {setTimeout(() => {resolve('p1')
    },1000)
});
let p2 = new Promise((resolve,reject) => {setTimeout(() => {reject('p2')
    },0)
});
Promise.race([p1,p2]).then((value) => {console.log(value)
},(err) => {console.log(err);        //p2
})

Promise.resolve(value)返回一个成功态,并且将 value 传递给对应的 then 方法;

Promise.reject(reason)返回一个失败态,并且将 reason 传递个对应的 then 方法。

Promise 的优缺点

优点 缺点
解决回调 无法监测进行状态
链式调用 新建立即执行且无法取消
减少嵌套 内部错误无法抛出

Promise 练习(Node 环境)

1. 等待状态

const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')
    }, 1000)
})
const promise2 = promise1.then(() => {return 'an error'})

console.log('promise1', promise1)
console.log('promise2', promise2)

setTimeout(() => {console.log('promise1', promise1)
    console.log('promise2', promise2)
}, 2000)

2. 状态改变

const promise = new Promise((resolve, reject) => {resolve('success1')
    reject('error')
    resolve('success2')
})
promise.then((res) => {console.log('then:', res)
})
.catch((err) => {console.log('catch:', err)
})

3. 链式调用

Promise.resolve(1)
    .then((res) => {console.log(res)
        return 2
    })
    .catch((err) => {return 3})
    .then((res) => {console.log(res)
    })

4. 链式调用

const promise = new Promise((resolve, reject) => {setTimeout(() => {console.log('executor')
        resolve('success')
    }, 1000)
})

const start = Date.now()
promise.then((res) => {console.log(res, Date.now() - start)
})
promise.then((res) => {console.log(res, Date.now() - start)
})
//Promise 的构造函数只执行一次,并且立即执行;promise 的 then 方法可以被多次调用,每次都返回新的 promise 实例。
const promise = new Promise((resolve, reject) => {setTimeout(() => {console.log('executor')
        resolve('success')
    }, 1000)
})
const promise2 = promise.then(res => {console.log(res);
})
promise2.then(res => {console.log(res)
})

5. 错误捕获

Promise.resolve()
    .then(() => {return new Error('error!!!')
    })
    .then((res) => {console.log('then:', res)
    })
    .catch((err) => {console.log('catch:', err)
    })
Promise.resolve()
    .then(() => {throw new Error('error');        //return Promise.reject('error')
    })
    .then((res) => {console.log('then:', res)
    })
    .catch((err) => {console.log('catch:', err)
    })

then 或 catch 方法返回一个错误对象并不会被捕获,只有在抛出错误或返回失败态时才会引起捕获。

6. 引用错误

const promise = Promise.resolve()
  .then(() => {return promise})
promise.catch(e => {console.log(e)
})
//[TypeError: Chaining cycle detected for promise #<Promise>]

返回值不能是 promise 实例本身,因为此时实例并未构建完成,造成死循环,因此抛出类型错误。

7. 事件环

process.nextTick(() => {console.log('nextTick')
})
Promise.resolve()
    .then(() => {console.log('then')
    })
setImmediate(() => {console.log('setImmediate')
})
console.log('end')

8. 穿透

Promise.resolve(1)
    .then(2)
    .then(Promise.resolve(3))
    .then(console.log)

then 和 catch 方法的参数是一个函数,若非函数则发生穿透

Promise A+ 规范

规范:https://promisesaplus.com/

该网站介绍了若需要实现 Promise 需要实现的规范方法。当多个库实现自己的 Promise 类时,为了能实现相应的效果,该规范提供了实现标准,并提供了 promises-aplus-tests 测试包测试实现能力。

后续将推出文章介绍如何实现自己的 Promise 类,敬请期待!

正文完
 0