promise是异步编程的一种解决方案,它出现的初衷是为了解决回调地狱的问题。html
打个比方,我须要:es6
--(延迟1s)--> 输出1 --(延迟2s)--> 输出2 --(延迟3s)--> 输出3
一般写法:编程
setTimeout(()=> { console.log('1'); setTimeout(()=> { console.log('2'); setTimeout(()=> { console.log('3'); }, 3000) }, 2000) }, 1000)
这样的多重的嵌套的回调被称为回调地狱,这样的代码可读性不好,不利于理解。segmentfault
若是用promise的话画风一转数组
function delay(time, num) { return new Promise((res, rej)=> { setTimeout(()=> { console.log(num); res(); }, time*1000) }); } delay(1, 1).then(()=> { return delay(2, 2); }).then(()=> { delay(3, 3); })
使用了promise的链式调用,代码结构更清晰。
是否是很棒?那还不赶快get起来~promise
调用方式以下:app
new Promise((resolve, reject)=> { if('some option') { resolve('some value'); } else { reject('some error'); } }).then( val=> { // ...
}, error=> { // ...
} )
Promise构造函数接收一个函数型参数fn,fn有两个参数,分别是:resolve、reject,Promise还有一个Promise.prototype.then方法,该方法接收两个参数,分别是成功的回调函数succ和失败的回调函数error。异步
在fn中调用resolve会触发then中的succ回调,调用reject会触发error回调。async
new Promise((res, rej)=> { res('happy') }).then(val=> { console.log(val); // happy
}); new Promise((res, rej)=> { rej('error!'); }).then(val=> {}, err=> { console.log(err); // error!
});
new Promise((res, rej)=> { res('a'); }).then(val=> { return 'b' }).then(val=> { console.log(val); // 'b'
}).then((val)=> { console.log(val); // 'undefined'
});
new Promise((res, rej)=> { res('a'); }).then(val=> { return 'b'; }).then(val=> { console.log(val); // 'b'
return 'c'; }).then({ // 并不是函数
name: 'lan' }).then((val)=> { console.log(val); // 'c'
});
let doSomething = function() { return new Promise((resolve, reject) => { resolve('返回值'); }); }; let doSomethingElse = function() { return '新的值'; } doSomething().then(function () { return doSomethingElse(); }).then(resp => { console.warn(resp); console.warn('1 =========<'); }); doSomething().then(function () { doSomethingElse(); }).then(resp => { console.warn(resp); console.warn('2 =========<'); }); doSomething().then(doSomethingElse()).then(resp => { console.warn(resp); console.warn('3 =========<'); }); doSomething().then(doSomethingElse).then(resp => { console.warn(resp); console.warn('4 =========<'); });
结合上面的讲解想想会输出什么?(答案及解析)异步编程
当Promise中的状态(pending ---> resolved or rejected)发生变化时才会执行then方法。
new Promise((res, rej)=> { res('a'); }).then(val=> { return 'b'; }); // 等同于
new Promise((res, rej)=> { res('a'); }).then(val=> { return new Promise((res, rej)=> { res('b'); }); });
new Promise((res, rej)=> { console.log('a'); res(''); }).then(()=> { console.log('b'); }); console.log('c'); // a c b
new Promise((res, rej)=> { console.log('a'); }).then(()=> { console.log('b'); }); console.log('c'); // a c
Promise还有四个静态方法,分别是resolve、reject、all、race,下面咱们一一介绍一下。
除了经过new Promise()的方式,咱们还有两种建立Promise对象的方法,Promise.resolve()至关于建立了一个当即resolve的对象。以下两段代码做用相同:
Promise.resolve('a'); new Promise((res, rej)=> { res('a'); });
固然根据传入的参数不一样,Promise.resolve()也会作出不一样的操做。
若是参数是 Promise 实例,那么Promise.resolve将不作任何修改、原封不动地返回这个实例。
thenable对象指的是具备then方法的对象,好比下面这个对象。
let thenable = { then: function(resolve, reject) { resolve(42); } };
Promise.resolve方法会将这个对象转为 Promise对象,而后就当即执行thenable对象的then方法。
若是参数是一个原始值,或者是一个不具备then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
Promise.resolve方法容许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
值得注意的一点是该静态方法是在本次事件轮询结束前调用,而不是在下一次事件轮询开始时调用。关于事件轮询能够看这里——>JavaScript 运行机制详解:再谈Event Loop
和Promise.resolve()相似,只不过一个是触发成功的回调,一个是触发失败的回调
Promise的all方法提供了并行执行异步操做的能力,而且在全部异步操做执行完后才执行回调。
function asyncFun1() { return new Promise((res, rej)=> { setTimeout(()=> { res('a'); }, 1000); }); } function asyncFun2() { return new Promise((res, rej)=> { setTimeout(()=> { res('b'); }, 1000); }); } function asyncFun3() { return new Promise((res, rej)=> { setTimeout(()=> { res('c'); }, 1000); }); } Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> { console.log(val); }); Promise.all([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> { console.log(val); // ['a', 'b', 'c']
});
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操做的并行执行的,等到它们都执行完后才会进到then里面。有了all,你就能够并行执行多个异步操做,而且在一个回调中处理全部的返回数据。
适用场景:打开网页时,预先加载须要用到的各类资源如图片、flash以及各类静态文件。全部的都加载完后,咱们再进行页面的初始化。
race()和all相反,all()是数组中全部Promise都执行完毕就执行then,而race()是一旦有一个Promise执行完毕就会执行then(),用上面的三个Promise返回值函数举例
Promise.race([asyncFun1(), asyncFun2(), asyncFun3()]).then((val)=> { console.log(val); // a
});
看了这么多关于Promise的知识,咱们来作一道题巩固一下。
写一个类Man实现如下链式调用 调用方式: new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
打印: 'hello, lan' -(等待3s)--> 'lan eat apple' -(等待5s)--> 'lan eat banana'
思路:
具体实现以下:
class Man { constructor(name) { this.name = name; this.sayName(); this.rope = Promise.resolve(); // 定义全局Promise做链式调用
} sayName() { console.log(`hello, ${this.name}`); } sleep(time) { this.rope = this.rope.then(()=> { return new Promise((res, rej)=> { setTimeout(()=> { res(); }, time*1000); }); }); return this; } eat(food) { this.rope = this.rope.then(()=> { console.log(`${this.name} eat ${food}`); }); return this; } } new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
ok!不知道你有没有看懂呢?若是能彻底理解代码那你的Promise能够通关了,顺便来个小思考,下面这种写法能够吗?和上面相比有什么区别?:
class Man1345 { constructor(name) { this.name = name; this.sayName(); } sayName() { console.log(`hello, ${this.name}`); } sleep(time) { this.rope = new Promise((res, rej)=> { setTimeout(()=> { res(); }, time*1000); }); return this; } eat(food) { this.rope = this.rope.then(()=> { console.log(`${this.name} eat ${food}`); }); return this; } } new Man('lan').sleep(3).eat('apple').sleep(5).eat('banana');
简单的说,第二段代码的执行结果是
'hello, lan' -(等待3s)--> 'lan eat apple' ---> 'lan eat banana'
为何会出现这种差异? 由于第二段代码每一次调用sleep都会new一个新的Promise对象,调用了两次sleep就new了两个Promise对象。这两个对象是异步并行执行,会形成两句eat同时显示。
和如下状况相似
var time1 = setTimeout(()=> { console.log('a'); }, 1000) var time2 = setTimeout(()=> { console.log('b'); }, 1000) // 同时输出 a b
抽象一点的讲解是:
// 第一段正确的代码的执行为
var p1 = new Promise().then('停顿3s').then('打印食物').then('停顿5s').then('打印食物'); // 第二段代码的执行行为,p一、p2异步并行执行
var p1 = new Promise().then('停顿3s').then('打印食物'); var p2 = new Promise().then('停顿5s').then('打印食物'); 总结
Promise的常常用到的地方:
Promise是咱们的好帮手,不过还有另外一种方法也能够作到,那就是async&await,能够多多了解一下。
参考资料