虽然项目中一直在用到promise,虽然以前也学习过promise,但是对于promise真的是没有很好的学以致用,有时候看到别人用promise的时候也是一脸懵逼,所以就决定花点时间再来好好研究一下promise到底是什么?应该怎么样用?
1、什么是promise?
Promise 是异步编程的一种解决方案,使得执行异步操作变得像同步操作一样。它可以被看成一个容器,容器里面是我们无法干预的,里面保存着某个未来才会结束的事件的结果。 Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及该异步操作的结果值。
基本用法如下
let p=new Promise((resolve,reject)=>{
resolve(123)//成功时执行resolve
reject("出错了")//失败时执行reject
})
p.then(res=>{
console.log(res);
},error=>{
console.log(error);
})
//123 因为状态变成resolve后就不会在改变了,所以reject不会被执行。
Promise 是一个构造函数, new Promise 返回一个 promise对象,Promise 对象是一个代理对象(代理一个值),被代理的值在Promise对象创建时可能是未知的。构造函数接收一个excutor执行函数作为参数, excutor有两个函数形参resolve和reject,分别作为异步操作成功和失败的回调函数。但该回调函数并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。resolve和reject函数在调用promise对象时才会被执行。
2、Promise对象的特点
1、对象的状态不受外界影响。 Promise对象有三种状态(resolve,reject,pending)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
2、状态一旦发生改变,就不会再变,任何时候都可以得到这个结果。 Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve(6);//不会再执行了。因为在setTimeout外面已经执行过resolve了,promise的状态已经改变了。
}, 0)
resolve(1);
});
p.then((arg) => {
console.log(arg);
});
//所以上面的输出为7、1、5(setTimeout是宏任务)
3、Promise的实例方法
1、Promise.prototype.then(onFulfilled, onRejected)
onFulfilled和onRejected分别是Promise实例对象 的成功和失败情况的回调函数。该方法返回一个新的 promise实例对象, 将以回调的返回值作为resolve的参数。并且then方法可以被同一个 promise 对象调用多次。
var promise1 = new Promise(function(resolve, reject) {
resolve('Success!');
});
promise1.then(function(value) {
console.log(value);//"Success!"
});
1、如果传入的 onFulfilled 参数类型不是函数,则会在内部被替换为(x) => x ,即原样返回 promise 最终结果的函数。
var p = new Promise((resolve, reject) => {
resolve('foo')
})
// 'bar' 不是函数,会在内部被替换为 (x) => x
p.then('bar').then((value) => {
console.log(value) // 'foo'
})
//等价于
p.then(res=>{
return res;
}).then((value) => {
console.log(value) // 'foo'
})
2、then方法允许链式调用。通过return将结果传递到下一个then,或者通过在then方法中新建一个promise实例,以resolve(value)或者reject(value)方法向下一个then方法传递参数(又是一个微任务)。
//例子1
Promise.resolve("foo")
.then(function (string) {
return string;
})
.then(function (string) {
setTimeout(function () {
string += 'baz';
console.log(string + "第二次调用");//foobaz第二次调用
}, 1)
return string;
})
.then(function (string) {
console.log(string + "第三次调用");//foo第三次调用
return string;
}).then(res => {
console.log(res + "第四次调用");//foo第四次调用
});
//例子2
Promise.resolve("foo")
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string);//foobarbaz
}, 1)
})
3、如果函数抛出错误或返回一个rejected的Promise,则调用将返回一个rejected的Promise。
//例子1
Promise.resolve()
.then( () => {
// 使 .then() 返回一个 rejected promise
throw 'Oh no!';
})
.then( () => {
console.log( 'Not called.' );
}, reason => {
console.error( 'onRejected function called: ', reason );
//onRejected function called: Oh no!
});
//例子2
Promise.reject()
.then( () => 99, () => 42 )
.then( solution => console.log( 'Resolved with ' + solution ) ); // Resolved with 42 //因为此时then方法接收的是上一个then方法reject方法中reject方法返回的值。
4、 promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once');//once
resolve('success')
}, 1000)
})
const start = Date.now()
promise.then((res) => {
console.log(res, Date.now() - start);//success 1007
})
promise.then((res) => {
console.log(res, Date.now() - start);//success 1007
})
2、Promise.prototype.catch(onRejected)
该方法的原码如下所示: Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}
this.then(null, onRejected)中由于null不为函数,所以实际执行为
this.then(res=>{
return res
}, onRejected);
该方法返回一个Promise, 并且处理reject的的情况。onRejected表示当Promise 被rejected时,被调用的一个Function。当这个回调函数被调用,新 promise 将以它的返回值来resolve下一个then函数继续被调用(正常情况是调用onFulfilled方法,除非在catch中抛出错误)。
//例子1
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success!"
throw 'oh, no!';
}).catch(function(e) {
console.log(e); // "oh, no!"
}).then(function(){
console.log('after a catch the chain is restored');//after a catch the chain is restored
return 3;
}, function () {
console.log('Not fired due to the catch');
})
1、在异步函数中抛出的错误不会被catch捕获到
var p2 = new Promise(function(resolve, reject) {
setTimeout(function() {
throw 'Uncaught Exception!';
}, 1000);
});
p2.catch(function(e) {
console.log(e); // 不会执行
});
2、在resolve()后面抛出的错误会被忽略(因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。)
var p3 = new Promise(function(resolve, reject) {
resolve();
throw 'Silenced Exception!';
});
p3.catch(function(e) {
console.log(e); // 不会执行
});
3、Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获(或者被then()捕获)为止。也就是说,错误总是会被下一个catch语句捕获。
Promise.reject(2).then((res) => {
console.log(res);
}).then().catch(res=>{
console.log(res);//2
});
Promise.reject(2).then((res) => {
console.log(res);
},(error)=>{
console.log(error);//2
}).then().catch(res=>{
console.log(res);//不会被捕获,没有任何输出
})
4、如果使用了catch语句,然后前面的then方法并没有报错,那么就相当于直接跳过该catch方法,下一个then方法接收上一个then方法中onFulfilled传过来的参数。
var p1 = new Promise(function(resolve, reject) {
resolve('Success');
});
p1.then(function(value) {
console.log(value); // "Success"
return value;
}).catch(function(e) {
console.log(e); // 没有任何输出
}).then(function(value){
console.log(value);//"Success"
//就好像跳过了catch语句,实际上catch执行的是this.then(null,onrejected)
//等同于
//this.then((res)=>{
// return res;
//},onrejected)
}, function () {
console.log('Not fired due to the catch');
})
5、.then 或者 .catch 中 return 一个 error 对象并不会抛出错误,所以不会被后续的 .catch 捕获。因为返回任意一个非 promise 的值都会被包裹成 promise 对象,即 return new Error('error!!!') 等价于 return Promise.resolve(new Error('error!!!'))。
Promise.resolve()
.then(() => {
return new Error('error!!!')
//或者改为
//return Promise.reject(new Error('error!!!'))或者
//throw new Error('error!!!') 才会被后面的catch语句捕捉到
})
.then((res) => {
console.log('then: ', res);//在这里输出
})
.catch((err) => {
console.log('catch: ', err)
})
6、.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log);
//输出1
//实际执行语句为
new Promise((resolve, reject) => {
resolve(1)
}).then(res => {
return res;
}).then((res) => {
return res;
}).then((res) => {
console.log(res);
})
3、Promise.prototype.finally(onFinally)
该方法返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行finally指定的回调函数。源码实现如下: Promise.prototype.finally = function (f) {
return this.then(function (value) {
return Promise.resolve(f()).then(function () {
return value;
});
}, function (err) {
return Promise.resolve(f()).then(function () {
throw err;
});
});
};
3、Promise的静态方法
1、Promise.all(iterable)
该方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。源码实现如下:
Promise.all=function(promises){
return new Promise(reslove,reject){
let arr=[];
promises.forEach((promise,i)=>{
promise.then(value=>{
arr.push(value);
if(i===promises.length){
resolve(arr);
}
},reject)
})
}
}
操作成功(Fulfillment)
1、如果传入的可迭代对象为空,Promise.all 会同步地返回一个已完成(resolved)状态的promise。
var p = Promise.all([]);
console.log(p);//Promise {<resolved>: Array(0)}
2、如果所有传入的 promise 都变为完成状态,或者传入的可迭代对象内没有 promise,Promise.all 返回的 promise 异步地变为完成。 3、在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。
var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([p1, p2, p3]).then(values => {
console.log(values); // [3, 1337, "foo"]
});
操作失败(Rejection)
1、如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。
var resolvedPromisesArray = [Promise.resolve(33), Promise.reject(44)];
var p = Promise.all(resolvedPromisesArray);
console.log(p);//Promise {<pending>}
// using setTimeout we can execute code after the stack is empty
//进入第二次循环
setTimeout(function(){
console.log('the stack is now empty');
console.log(p);//Promise {<rejected>: 44}
});
2、Promise.race(iterable)
原码实现如下: Promise.race = function(promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(resolve, reject);
});
});
}
该方法返回一个 promise,一旦迭代器中的某个子promise执行了成功或失败操作,父promise对象也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象。如果传的迭代(iterable)是空的,则返回的 promise 将永远等待。
let p=Promise.race([1,2]).then(value=>{
console.log(value);//1
});
let p=Promise.race([]);
console.log(p);//Promise {<pending>}
3、Promise.resolve(value)
该方法的作用就是将现有对象转为 Promise 实例,并且该实例的状态为resolve。 该方法的源码实现如下:
Promise.resolve (value) {
// 如果参数是MyPromise实例,直接返回这个实例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
参数value可以是一个Promise对象,或者是一个thenable,也可以就是一个字符串等常量,还可以为空。该方法返回一个以给定值解析后的Promise 对象。
Promise.resolve('foo')
// 等价于
return new MyPromise(resolve => resolve(value))
if (value instanceof MyPromise) {
return value
}else{
return new MyPromise(resolve => resolve(value))
}
1、如果这个值是个thenable(即带有then方法),返回的promise会"跟随"这个thenable的对象(从它们的运行结果来看,返回的就好像是这个thenable对象一样。但实际上返回的是一个Promise对象),采用它的最终状态(指resolved/rejected/pending/settled)。
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
thenable.then(res=>{
console.log(res);//42
})
//上面的代码可以被解析为
let p1=new Promise(resolve=>{
reslove(thenable);
})
p1.then(function(value) {
console.log(value); // 42
});
2、如果传入的value本身就是promise对象,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
var original = Promise.resolve('我在第二行');
original.then(function(value) {
console.log('value: ' + value);//'我在第二行'
});
var cast = Promise.resolve(original);
cast.then(function(value) {
console.log('value: ' + value);//'我在第二行'
});
console.log('original === cast ? ' + (original === cast));//true
3、参数不是具有then方法的对象,或根本就不是对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
//等同于
const p=new Promise(resolve=>{
resolve('Hello');
})
p.then(res=>{
console.log(res) // Hello
});
4、不带有任何参数。直接返回一个resolved状态的 Promise 对象。
const p = Promise.resolve();
console.log(p);//Promise {<resolved>: undefined}
4、Promise.rejected(value)
返回一个新的 Promise 实例,该实例的状态为rejected。 const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s);// 出错了
});
4、Promise代码实现
//初始版
function Promise1(executor) {
let self = this;
self.status = "pending";
self.value = undefined;
self.reason = undefined;
function resolve(value) {
if (self.status === "pending") {
self.value = value;
self.status = "resolve";
}
}
function reject(reason) {
if (self.status === "pending") {
self.reason = reason;
self.status = "reject"
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise1.prototype.then = function (onFullfilled, onRejected) {
let status = this.status;
switch (status) {
case "resolve":
onFullfilled(this.value);
break;
case "reject":
onRejected(this.reason);
break;
default:
break;
}
}
let p = new Promise1((resolve, reject) => {
resolve(123);
});
p.then(res => {
console.log(res);
})
//进阶版 处理异步
function Promise2(executor) {
let self = this;
self.status = "pending";
self.value = undefined;
self.reason = undefined;
self.onFullfilledArray = [];
self.onRejectedArray = [];
function resolve(value) {
if (self.status === "pending") {
self.value = value;
self.status = 'resolve';
self.onFullfilledArray.forEach(fn => {
fn(self.value);
})
}
}
function reject(reason) {
if (self.status === "pending") {
self.reason = reason;
self.status = "reject";
self.onFullfilledArray.forEach(fn => {
fn(this.reason);
})
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e);
}
}
Promise2.prototype.then = function (onFullfilled, onRejected) {
let status = this.status;
switch (status) {
case "pending":
this.onFullfilledArray.push(() => {
onFullfilled(this.value);
})
this.onRejectedArray.push(() => {
onRejected(this.reason);
});
break;
case "resolve":
onFullfilled(this.value);
break;
case "reject":
onRejected(this.reason);
break;
default:
break;
}
}
let p = new Promise2((resolve, reject) => {
setTimeout(function () { resolve(1) }, 1000)
});
p.then(res => {
console.log(res);
})
//进阶2 链式调用
function Promise3(executor) {
if (typeof executor !== "function") {
throw new Error("executor必须是一个函数");
}
let self = this;
self.status = "pending";
self.value = "";
self.reason = "";
self.onFullfilledArray = [];
self.onRejectedArray = [];
function resolve(value) {
if (self.status === "pending") {
self.status = "resolve";
self.value = value;
self.onFullfilledArray.forEach(fn => {
fn(this.value);
});
}
}
function reject(reason) {
if (self.status === "pending") {
self.status = "resolve";
self.reason = reason;
self.onRejectedArray.forEach(fn => {
fn(this.reason);
});
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e);
}
}
Promise3.prototype.then = function (onFullfilled, onRejected) {
let status = this.status;
let promise;
switch (status) {
case "pending":
promise = new Promise3((resolve, reject) => {
this.onFullfilledArray.push(() => {
let value = onFullfilled(this.value);
try {
resolve(value);
} catch (e) {
reject(e);
}
});
this.onRejectedArray.push(() => {
let value = onRejected(this.reason);
try {
resolve(value);
} catch (e) {
reject(e);
}
});
});
break;
case "resolve":
promise = new Promise3((resolve, reject) => {
let value = onFullfilled(this.value);
try {
resolve(value);
} catch (e) {
reject(e);
}
});
break;
case "reject":
promise = new Promise3((resolve, reject) => {
let value = onRejected(this.reason);
try {
resolve(value);
} catch (e) {
reject(e);
}
});
break;
}
return promise;
}
后面再复杂的情况暂时就不考虑了。
参考链接
1、ECMAScript入门
2、Promise——MDN
3、Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)
4、这一次,彻底弄懂 JavaScript 执行机制
5、Promise 必知必会(十道题)