一、定义
特点
- 只有有三种状态:padding(进行中)、fulfilled(成功)、rejected(失败)
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态只有从padding改变到fulfilled或者refected,两种改变。
存在的缺点
-
无法取消Promise,一旦新建它就会立即执行,无法中途取消。
-
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
-
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
二、基本用法
构造函数
ES6中,Promise对象是一个构造函数,用来生成Promise实例。
Promise构造函数接受一个函数作为参数,接受两个参数,分别是resolve和reject两个函数。
var p = new Promise((resolve, reject) => {})
- resolve :将Promise的状态从padding改变成fufilled,异步操作执行成功后的回调函数;
- reject:将Promise的状态从padding改变成rejected,异步操作执行失败后的回调函数。
then 链式调用
在Promise实例生产后,可以用then方法分别指定了fufilled状态和rejected状态的回调函数。
p.then(resolveFn, rejectFn)
第一个对应resolve的回调函数,第二个对应reject的回调函数。第二个参数是可选的,不一定要提供。
const p = new Promise((resolve, reject) => {
console.log('new Promise()');
resolve();
});
p.then(() => {
console.log('resolve()');
}, () => {
console.log('reject()');
});
catch的调用
promise.catch(
error = > errorHandle(error)
)
其实这就是相当于promise.then(null, errorHandle)
。 .catch
会捕获到整个异步操作中,或者是一系列连续的异步操作链中的出现的任何类型的错误,一旦抛出错误,则会直接转入到.catch中进行错误处理。
new Promise((resolve,reject)=> {
throw(new Error("error"))
resolve("resolve")
}).then((e) => {
console.log('resolve', e)
}).catch((e) => {
console.log('error', e) // error Error
});
finally的用法
当promise的状态确定时,即无论拿到的是正常结果还是错误信息,总会执行finally。用finally可以做一些清理操作,比如发起网络请求后,可以停止loading显示。
new Promise((resolve,reject)=> {
resolve('resolve');
} ).then((e) => {
console.log('resolve', e)
}).catch((e) => {
console.log('reject', e)
}).finally(() => {
console.log('finally')
})
// resolve resolve
// finally
all
all() 接受数组作为参数。成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
p1,p2,p3都是Promise的实例对象,p要变成Resolved状态需要p1,p2,p3状态都是Resolved,如果p1,p2,p3至少有一个状态是Rejected,p的状态就变成Rejected。
p 要变成 Resolved 状态执行 then() 需要确保每个 Promise 都返回 Resolved,也就是说会等待每个 Promise 执行完毕。不过只要有个状态是 Rejected, 那么就马上执行 catch(), 不会等待其他还未有返回状态的 Promise。
Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。
var p1 = new Promise((resolve,reject)=> {
resolve('p1 resoleve');
})
var p2 = new Promise((resolve,reject)=> {
setTimeout(() => {
resolve('p2 resoleve');
}, 2000)
})
var p3 = new Promise((resolve,reject)=> {
resolve('p3 resoleve');
// reject('p3 reject');
})
var p = Promise.all([p1,p2,p3]);
p.then(e => {
console.log(e); // 2s 后按顺序输出 [ 'p1 resoleve', 'p2 resoleve', 'p3 resoleve' ]
}).catch((e) => {
console.log(e) // 当p3 返回 Rejected 状态后,马上输出 'p3 reject'
});
race
race() 接受数组作为参数。
p1,p2,p3都是Promise的实例对象,只需要获得先返回的结果即可,在这个例子中p1,p2,p3三者的运行速度一样,所以取决于数组中第一位 p1的运行状态,p2,p3仍在继续执行,但执行结果将被丢弃。
当p1 是resolve, p的状态就是resolve,当p1 是reject, p的状态就是reject。
var p1 = new Promise((resolve,reject)=> {
resolve('p1');
} )
var p2 = new Promise((resolve,reject)=> {
resolve('p2');
} )
var p3 = new Promise((resolve,reject)=> {
reject('p3');
} )
var p = Promise.race([p1,p2,p3]);
p.then((res) => {
console.log('resolve', res) // 当 resolve 状态先返回
}).catch((err) => {
console.log('reject', err)/ / 当 reject 状态先返回
});
三、深入了解 Promise
Promise的创建
Promise创建时,会传给promise一个称为excutor
执行器的函数。这个excutor我们可以理解为生产者的生产过程函数。这个函数含有两个参数resolve和reject,这俩参数也同样是函数,用来传递异步操作的结果。语法如下:
let promise = new Promise(function(resolve, reject) {
// executor
})
判断一个对象是否是Promise
let promise = new Promise(function(resolve, reject) {})
console.log(promise instanceof Promise) // true
创建过程
- 在promise对象创建时,excutor会立即执行。
- 在resolve和reject是JS引擎自动创建的函数,我们无需自己创建,只需将其作为参数传入就好。
- 创建的promise的内部状态是个对象,初始时为:
{ state, //pending result, //undefined }
一旦exucutor执行完,要么产生value,要么产生error,此时会立即调用resolve(当产生value时)或者调用reject(当产生error)时,内部状态也会随之改变,如下图所示:
注意,当excutor里面即使调用了多个resolve和reject,其最终还是只执行一个,其他的都被忽略掉。
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("error")); // ignored
setTimeout(() => resolve("error")); // ignored
});
Promise resolve()
有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。
Promise.resolve('resolve')
// 相当于
new Promise(resolve => {
resolve('resolve');
});
Promise reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
Promise.reject('reject');
// 相当于
new Promise((resolve, reject) => {
reject('reject');
});
var p = new Promise((resolve,reject)=> {
reject();
})
p.then(() => {
console.log('1111')
}).catch(() => {
console.log('222')
});
// '2222"
Promise链式调用
当有一系列的异步操作需要一个接一个执行时,可以使用promise的调用链。
举个例子,我们用fetch这个方法去发起网络请求,fetch()返回的是一个promise对象,那么我们可以对其连续地调用.then来进行一步步连续地异步操作。
注意:promise.then()
返回的是一个新的promise
对象,所以我们才可以继续对其调用.then
。
fetch(url)
.then(response => response.json())
.then(user => fetch(url)
.then(response => response.json())
}).catch(error => console.log(error));
promise.then( handleFunction ) 中的handleFunction可以返回立即值,也可以返回promise对象。
如果返回立即值,则可以直接把结果传入到下一步的.then进行处理,但是如果返回的是promise对象,那么一定要等到这个返回的promise处理完,拿到结果后,才会进行下一步的 then处理。
Promise.resoleve参数
1.参数是promise实例
直接返回这个实例本身,不做处理。
const p = new Promise((resolve, reject) => {
resolve("111")
})
new Promise((resolve, reject) => {
resolve(p)
}).then(res => {
console.log(res) // 111
})
2.参数是一个thenable对象
thenable对象指的是具有then方法的对象,则会将此对象生成为一个promise,状态为resolved,并调用其then方法。比如下面这个对象。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let 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,并将改参数传入resolved函数处理。
const p = Promise.resolve('111');
p.then(function (s){
console.log(s)
});
// 111
4.参数为空
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
Promise.resolve().then(function () {
console.log('111');
});
// 立即输出 111
四、实现 Promise
基础版
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = null
this.onRejectedFn = null
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn(this.value)
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn(this.reason)
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
this.onFullFilledFn = onFullFilled
this.onRejectedFn = onRejected
}
let promise = new MyPromise((resolve, reject) => {
resolve("同步任务执行")
})
promise.then((val) => {console.log(val)}) // 同步任务执行
这样就是 Promise 的基础版,可以创建promise对象实例,执行成功回调函数和失败回调函数。
在reslove和reject里面用setTimeout进行包裹,是为了使其到then方法执行之后再去执行。要不然会报错 "this.onFullFilledFn is not a function"
。
三种状态
MyPromise.prototype.then = function (onFullFilled, onRejected) {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
onFullFilled(this.value)
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onFullFilledFn = onFullFilled
this.onRejectedFn = onRejected
}
}
then 支持链式操作
现在版本最多只能注册一个回调,我们来实现支持链式回调。
- 首先存储回调时要改为使用数组;
- 执行回调时,也要改成遍历回调数组执行回调函数;
- then 方法返回一个 Promise。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn(this.value))
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn(this.reason))
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
return new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
onFullFilled(this.value)
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onFullFilledFn.push(onFullFilled)
this.onRejectedFn.push(onRejected)
}
})
}
支持串行异步任务
目前then方法里只能传入同步任务,但是我们平常用promise.then方法里一般是异步任务,因为我们用promise主要用来解决一组流程化的异步操作。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn(this.value))
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn(this.reason))
})
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
if (this.status === PENDING) {
this.onFullFilledFn.push(() => {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
this.onRejectedFn.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
}
})
return promise
}
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
return reject(new Error('循环引用'))
}
// x 是除了 null 以外的对象或者函数
let called
if (x && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
}
实现 catch / all
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
constructor (fn) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFullFilledFn = []
this.onRejectedFn = []
// 在使用es6 的promise时,可以传入一个异步任务,也可以传入一个同步任务,利用setTimeout特性将具体执行放到then之后
let resolve = (value) => {
if (this.status === PENDING) {
setTimeout(() => {
this.status = FULFILLED
this.value = value
this.onFullFilledFn.forEach(fn => fn())
})
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectedFn.forEach(fn => fn())
}
}
try {
fn(resolve, reject)
} catch (err) {
reject(err)
}
}
catch (onRejected) {
// catch 方法就是then方法没有成功的简写
return this.then(null, onRejected);
}
all (promises) {
//promises是一个promise的数组
return new MyPromise(function (resolve, reject) {
let arr = []; //arr是最终返回值的结果
let i = 0; // 表示成功了多少次
function processData(index, data) {
arr[index] = data;
if (++i === promises.length) {
resolve(arr);
}
}
for (let i = 0; i < promises.length; i++) {
promises[i].then(function (data) {
processData(i, data)
}, reject)
}
})
}
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
const promise = new MyPromise((resolve, reject) => {
// 如果状态是fulfilled,直接执行成功回调,并将成功值传入
if (this.status === FULFILLED) {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
// 如果状态是rejected,直接执行失败回调,并将失败原因传入
if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = onRejected(this.reason)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
}
if (this.status === PENDING) {
this.onFullFilledFn.push(() => {
setTimeout(() => {
try {
const x = onFullFilled(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
this.onRejectedFn.push(() => {
setTimeout(() => {
try {
const x = onRejected(this.value)
resolvePromise(promise, x, resolve, reject)
} catch(err) {
reject(err)
}
})
})
}
})
return promise
}
function resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
return reject(new Error('循环引用'))
}
// x 是除了 null 以外的对象或者函数
let called
if (x && (typeof x === 'function' || typeof x === 'object')) {
try {
const then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, err => {
if (called) return
called = true
reject(err)
})
} else {
resolve(x)
}
} catch (err) {
if (called) return
called = true
reject(err)
}
} else {
resolve(x)
}
}
参考链接:
ES6 - 整理一下Promise 的用法
JavaScript 之 Promise
Promise 原理解析与源码实现
这一次,彻底弄懂 Promise 原理
Promise.resolve()与new Promise(r => r(v))