虽然项目中一直在用到 promise,虽然以前也学习过 promise,但是对于 promise真的是没有很好的学以致用,有时候看到别人用promise的时候也是一脸懵逼,所以就决定花点时间再来好好研究一下promise到底是什么?应该怎么样用?
1、什么是promise?
Promise 是异步编程的一种解决方案。它可以被看成一个容器,容器里面是我们无法干预的,里面保存着某个未来才会结束的事件的结果。 Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及该异步操作的结果值。
let p=new Promise(function excutor(resolve,reject){
//resolve和reject函数只执行一个
resolve(123)//成功时执行resolve
reject("出错了")//失败时执行reject
})
p.then(res=>{
console.log(res);
},error=>{
console.log(error);
})
//123 执行resolve函数后,状态由pending状态变成了fulfilled。再去调用reject函数时,因为状态已经不是pending了,所以reject里面的内容不会执行了 Promise 是一个构造函数, new Promise 返回一个promise对象,构造函数接收一个excutor执行函数作为参数, excutor有两个函数形参resolve和reject,分别作为异步操作成功和失败的回调函数。但该回调函数并不是立即返回最终执行结果,通过then 函数来获取执行结果。
2、Promise对象的特点
1、对象的状态不受外界影响。
Promise对象有三种状态fulfilled,rejected,pending)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
2、状态一旦发生改变,就不会再变,任何时候都可以得到这个结果。
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
Promise的优点:
- 将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise对象提供统一的接口,使得控制异步操作更加容易。- 解决了异步并发问题(Promise.all)。
Promise的不足:
- 1、无法取消
Promise,一旦新建它就会立即执行,无法中途取消。 - 2、如果不设置回调函数,
Promise内部抛出的错误,不会反应到外部。 - 3、当处于
pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
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、53、Promise的实例方法
1、Promise.prototype.then(onFulfilled, onRejected)
onFulfilled和onRejected分别是Promise实例对象成功和失败情况的回调函数。- 该方法返回一个新的
promise实例对象。 - 将以回调的返回值(return,只要不报错),作为
then函数调用时onFulfilled回调函数的参数。 并且then方法可以被同一个promise对象调用多次。
let p=new Promise(function excutor(resolve,reject){
resolve(20);//输出 成功了-> 20;成功了->200;失败了 2000
// reject(20); //输出 失败了->20;成功了->2;失败了->20
})
p.then(res=>{
console.log(`成功了->${res}`);
return res*10;
},error=>{
console.log(`失败了->${error}`);
return (error/10);
}).then(res=>{
console.log(`成功了->${res}`);
return Promise.reject(res*10);
},error=>{
console.log(`失败了->${error}`)
return error/10;
}).then(res=>{
console.log(`成功了->${res}`);
return res*10;
},error=>{
console.log(`失败了->${error}`);
return error/10;
})1、如果传入的
onFulfilled参数类型不是函数,则会在内部被替换为(x) => x,即原样返回promise最终结果的函数。
- 例子1
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
Promise.reject(10).then(null,error=>error).then(
res=>{
console.log(`成功了->${res}`);
},
error=>{
console.log(`失败了->${error}`);
return error/10;
}
)
Promise.resolve(10).then(null /*res=>res*/,error=>error).then(
res=>{
console.log(`成功了->${res}`);
},
error=>{
console.log(`失败了->${error}`);
return error/10;
}
)
// 上面的两个都是输出 成功了->10- 例子3
Promise.reject(10).then(null /*res=>res*/,null /*err=>Promise.reject(error) */).then(
res=>{
console.log(`成功了->${res}`);
},
error=>{
console.log(`失败了->${error}`);
return error/10;
}
)
//输出 失败了->102、
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);//foobaz
}, 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=[];
let count=0;
promises.forEach((promise,i)=>{
promise.then(value=>{
arr.push(value);
if(++count===promises.length){
resolve(arr);
}
},err=>{
reject(err)
})
})
})
}操作成功(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));//true3、参数不是具有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代码实现
1、初始版
function Promise1(executor) {
let self = this;
self.PromiseState = "pending";
self.PrimiseResult = undefined;
function resolve(value) {
if (self.PromiseState === "pending") {
self.PrimiseResult = value;
self.PromiseState = "fulfilled";
}
}
function reject(reason) {
if (self.PromiseState === "pending") {
self.PrimiseResult = reason;
self.PromiseState = "rejected"
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
Promise1.prototype.then = function (onFullfilled, onRejected) {
let PromiseState = this.PromiseState;
switch (PromiseState) {
case "resolve":
onFullfilled(this.PrimiseResult);
break;
case "reject":
onRejected(this.PrimiseResult);
break;
default:
break;
}
}
let p = new Promise1((resolve, reject) => {
resolve(123);
});
p.then(res => {
console.log(res);
})2、进阶版——异步处理
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);
})3、 进阶版——链式调用
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;
}4、最终版
const { reject } = require('./Promise');
const FULLFILLED = 'fullfiled', REJECTED = "rejected", PENDING = "pending";
let id = 1;
function Promise (executor) {
this.id = id++;
this.status = PENDING;
this.value = undefined;
this.onFullfiledArray = [];
this.onRejectedArray = [];
const that = this;
function resolve (value) {
if (that.status === PENDING) {
that.status = FULLFILLED;
that.value = value;
that.onFullfiledArray.forEach(fn => fn(value));
}
}
function reject (reason) {
if (that.status === PENDING) {
that.status = REJECTED;
that.value = reason;
that.onRejectedArray.forEach(fn => fn(reason));
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
Promise.resolve = function (value) {
return new Promise((resolve, _) => {
resolve(value)
})
}
Promise.reject = function (reason) {
return new Promise((_, reject) => {
reject(reason);
})
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
const then = item.then;
if (typeof then === 'function') {
then.call(item, y => {
resolveValue(y, index)
}, r => {
reject(r);
})
}
} else {
resolveValue(item, index)
}
});
let res = [], count = 0;;
function resolveValue (value, index) {
++count;
res[index] = value;
if (count === promises.length) {
resolve(res);
}
}
})
}
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
const then = item.then;
if (typeof then === 'function') {
then.call(item, y => {
resolve(y);
}, r => {
reject(r);
})
}
} else {
resolve(item);
}
});
})
}
function resolvePromise (promise, x, resolve, reject) {
if (x === promise) {
return reject(new TypeError("循环引用"))
}
if ((x !== null && typeof x === 'object') || typeof x === 'function') {
let called = false;//用于避免重复调用
try {
const then = x.then;
if (typeof then === 'function') {
then.call(x, y => {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
}, r => {
if (called) return;
called = true;
reject(r);
})
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
Promise.prototype = {
constructor: Promise,
then (onFulfilled, onRejected) {
let promise;
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
switch (this.status) {
case FULLFILLED:
return promise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
});
case REJECTED:
return promise = new Promise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
});
case PENDING:
return promise = new Promise((resolve, reject) => {
this.onFullfiledArray.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
});
this.onRejectedArray.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
})
})
});
}
},
catch (onRejected) {
return this.then(null, onRejected);
}
}
/**
* Promise/A+规范测试
* npm i -g promises-aplus-tests
* promises-aplus-tests Promise.js
*/
Promise.deferred = function () {
const defer = {};
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve;
defer.reject = reject;
});
return defer;
}
module.exports = Promise;//测试
const p1 = new Promise((resolve, reject) => {
resolve(100)
})
const p2 = p1.then((value) => { // 此时p1.status 由pending状态 => fulfilled状态
return p2;
}, err => {
console.log("failed", err);
}).then(value => {
//没有输出
return value + 100;
}, err => {
console.log(err);
})
p2.then(data => {
console.log(data);
}, err => {
console.log('err', err);
})
console.log('全局执行栈中同步代码');
//上面的代码除了输出 '全局执行栈中同步代码'之外,不会有其他的任何输出 ,因为return p2后 p2的状态一直是pending参考链接
1、ECMAScript入门
2、Promise——MDN
3、Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)
4、这一次,彻底弄懂 JavaScript 执行机制
5、Promise 必知必会(十道题)
