3):Promise.all()
Promise.all()接收一个参数,它必须是可以迭代的,比如数组。
它通常用来处理一些并发的异步操作,即它们的结果互不干扰,但是又需要异步执行。它最终只有两种状态:成功或者失败。
指的是将数组中所有的任务执行完成之后, 才执行.then 中的任务
它的状态受参数内各个值的状态影响,即里面状态全部为fulfilled时,它才会变成fulfilled,否则变成rejected。
成功调用后返回一个数组,数组的值是有序的,即按照传入参数的数组的值操作后返回的结果。
如下:
constp1=newPromise((resolve,reject)=>{ setTimeout(()=>{ resolve(console.log('p1 任务1')) },1000) }) .then( data=> { console.log('p1 任务2') }) .then( res=> { console.log('p1 任务3') }) .catch( err=>{ throwerr} ) constp2=newPromise((resolve,reject)=>{ resolve(console.log('p2 任务1')) }).then( data=> { console.log('p2 任务2') } ).catch( err=> { throwerr } ) //只有在p1,p2都执行完后才会执行then里的内容Promise.all([p1,p2]) .then(()=>console.log('done'))
4):Promise.race()
Promise.race()和Promise.all()类似,都接收一个可以迭代的参数,但是不同之处是Promise.race()的状态变化不是全部受参数内的状态影响,一旦参数内有一个值的状态发生的改变,那么该Promise的状态就是改变的状态。就跟race单词的字面意思一样,谁跑的快谁赢。如下:
varp1=newPromise(function(resolve, reject) { setTimeout(resolve, 300, 'p1 doned'); }); varp2=newPromise(function(resolve, reject) { setTimeout(resolve, 50, 'p2 doned'); }); varp3=newPromise(function(resolve, reject) { setTimeout(reject, 100, 'p3 rejected'); }); Promise.race([p1, p2, p3]).then(function(data) { // 显然p2更快,所以状态变成了fulfilled// 如果p3更快,那么状态就会变成rejectedconsole.log(data); // p2 doned}).catch(function(err) { console.log(err); // 不执行});
5):Promise.resolve()
Promise.resolve()接受一个参数值,可以是普通的值,具有then()方法的对象和Promise实例。正常情况下,它返回一个Promise对象,状态为fulfilled。但是,当解析时发生错误时,返回的Promise对象将会置为rejected态。如下:
// 参数为普通值varp4=Promise.resolve(5); p4.then(function(data) { console.log(data); // 5}); // 参数为含有then()方法的对象varobj= { then: function() { console.log('obj 里面的then()方法'); } }; varp5=Promise.resolve(obj); p5.then(function(data) { // 这里的值时obj方法里面返回的值console.log(data); // obj 里面的then()方法}); // 参数为Promise实例varp6=Promise.resolve(7); varp7=Promise.resolve(p6); p7.then(function(data) { // 这里的值时Promise实例返回的值console.log(data); // 7}); // 参数为Promise实例,但参数是rejected态varp8=Promise.reject(8); varp9=Promise.resolve(p8); p9.then(function(data) { // 这里的值时Promise实例返回的值console.log('fulfilled:'+data); // 不执行}).catch(function(err) { console.log('rejected:'+err); // rejected: 8});
6):Promise.reject()
Promise.reject()和Promise.resolve()正好相反,它接收一个参数值reason,即发生异常的原因。此时返回的Promise对象将会置为rejected态。如下:
varp10=Promise.reject('手动拒绝'); p10.then(function(data) { console.log(data); // 这里不会执行,因为是rejected态}).catch(function(err) { console.log(err); // 手动拒绝}).then(function(data) { // 不受上一级影响console.log('状态:fulfilled'); // 状态:fulfilled});
总之,除非Promise.then()方法内部抛出异常或者是明确置为rejected态,否则它返回的Promise的状态都是fulfilled态,即完成态,并且它的状态不受它的上一级的状态的影响。
2.gengerator函数
在异步编程中,还有一种常用的解决方案,它就是Generator生成器函数。顾名思义,它是一个生成器,它也是一个状态机,内部拥有值及相关的状态,生成器返回一个迭代器Iterator对象,我们可以通过这个迭代器,手动地遍历相关的值、状态,保证正确的执行顺序。
es6 提供的 generator函数
总得来说就三点:
*在function关键字后加一个* , 那么这个函数就称之为generator函数
*函数体有关键字 yield , 后面跟每一个任务 , 也可以有return关键字, 保留一个数据
*通过next函数调用, 几个调用, 就是几个人任务执行
(1).简单使用
Generator的声明方式类似一般的函数声明,只是多了个*号,并且一般可以在函数内看到yield关键字
function*showWords() { yield'one'; yield'two'; return'three'; } varshow=showWords(); show.next() // {done: false, value: "one"}show.next() // {done: false, value: "two"}show.next() // {done: true, value: "three"}show.next() // {value: underfined, done: true}
如上代码,定义了一个showWords的生成器函数,调用之后返回了一个迭代器对象(即show)
调用next方法后,函数内执行第一条yield语句,输出当前的状态done(迭代器是否遍历完成)以及相应值(一般为yield关键字后面的运算结果)
每调用一次next,则执行一次yield语句,并在该处暂停,return完成之后,就退出了生成器函数,后续如果还有yield操作就不再执行了
当然还有以下情况:(next()数量小于yield)
function*g1(){ yield'任务1'yield'任务2'yield'任务3'return'任务4'} constg1done=g1() console.log(g1done.next()) //{ value: '任务1', done: false }console.log(g1done.next()) //{ value: '任务2', done: false }
(2).yield和yield*
有时候,我们会看到yield之后跟了一个*号,它是什么,有什么用呢?
类似于生成器前面的*号,yield后面的星号也跟生成器有关,举个大栗子:
function*showWords() { yield'one'; yieldshowNumbers(); return'three'; } function*showNumbers() { yield10+1; yield12; } varshow=showWords(); show.next() // {done: false, value: "one"}show.next() // {done: false, value: showNumbers}show.next() // {done: true, value: "three"}show.next() // {done: true, value: undefined}
增添了一个生成器函数,我们想在showWords中调用一次,简单的 yield showNumbers()之后发现并没有执行函数里面的yield 10+1
因为yield只能原封不动地返回右边运算后值,但现在的showNumbers()不是一般的函数调用,返回的是迭代器对象
所以换个yield* 让它自动遍历进该对象
function*showWords() { yield'one'; yield*showNumbers(); return'three'; } function*showNumbers() { yield10+1; yield12; } varshow=showWords(); show.next() // {done: false, value: "one"}show.next() // {done: false, value: 11}show.next() // {done: false, value: 12}show.next() // {done: true, value: "three"}
要注意的是,这yield和yield* 只能在generator函数内部使用,一般的函数内使用会报错
function showWords() { yield 'one'; // Uncaught SyntaxError: Unexpected string }
虽然换成yield*不会直接报错,但使用的时候还是会有问题,因为’one'字符串中没有Iterator接口,没有yield提供遍历
functionshowWords() { yield*'one'; } varshow=showWords(); show.next() // Uncaught ReferenceError: yield is not defined
在开发中,我们常常需要请求多个地址,为了保证顺序,引入Promise对象和Generator生成器函数,看这个简单的栗子:
varurls= ['url1', 'url2', 'url3']; function*request(urls) { urls.forEach(function(url) { yieldreq(url); }); // for (var i = 0, j = urls.length; i < j; ++i) {// yield req(urls[i]);// }} varr=request(urls); r.next(); functionreq(url) { varp=newPromise(function(resolve, reject) { $.get(url, function(rs) { resolve(rs); }); }); p.then(function() { r.next(); }).catch(function() { }); }
上述代码中forEach遍历url数组,匿名函数内部不能使用yield关键字,改换成注释中的for循环就行了