11、ES6 之Promise
来源:互联网 发布:linux 百度网盘 编辑:程序博客网 时间:2024/06/06 23:52
Promise 等待 //resolve() 代表成功后往后执行成功对应的函数 reject()失败后往后执行失败对应的函数
let p = function (data){ return new Promise( (resolve,reject) => { if(data==1){ setTimeout(() => { resolve("成功");//如果不加入resolve成功后就会一直处于等待状态 },2000); }else{ setTimeout(() => { reject("失败");//如果不加入rejec失败后就会一直处于等待状态 },2000); } });};p(1).then(function (data){//第一个函数为成功后执行的函数 console.log(data);},function (data){//第二个函数为失败后执行的函数 console.log(data);});p(2).then(function (data){ console.log(data);},function (data){ console.log(data);});
let p = function (data){ return new Promise((resolve,reject) => { setTimeout(() => { console.log(data); resolve( "成功" ); },2000) });};// .then() 等待上一个Promise执行完之后才会执行的方法p(1).then(function (data){ /* 这里必须是一个Promise对象,如果不是Promise对象 之后的then会异步执行 */ console.log(data); return p(2);//如果这里不返回一个Promise对象,后面的then会异步执行}).then(function (data){ console.log(data); p(3)});
Promise使用场景
要弄清原理,必须要非常清楚Promise想实现什么。比如现在有两件事情,第一件事情是要洗衣服,第二件事情是晾衣服。
function wash(){ console.log('开始洗衣服...'); setTimeout(()=>{ console.log('洗完了!'); return '一堆洗干净的衣服'; }, 2000);}function hang(clothes){ console.log('开始晾衣服...'); /*...晾衣服中...*/ console.log(clothes+'晾完了!');}
晾衣服hang(clothes)是一定要等洗衣服wash()结束,然后接收到洗好了的衣服clothes以后,才能执行的。类似这种有明确先后执行顺序,并且可能会有依赖关系的(没有前面执行完返回的结果后面就处理不下去)场景,就是Promise的用武之地。
Promise语法
先看下怎么用Promise来完成先洗衣服后晾干这两步的。
第一步:
var promise = new Promise(wash);告诉Promise立刻执行wash开始洗衣服。
第二步:
promise.then(hang);告诉Promise等wash结束以后执行hang开始晾衣服。
Promise用起来就是这么简单!这么清爽!如果等晾干以后还要收衣服的话,就继续再后面加:
promise.then(hang).then(pickup); //pickup就是收衣服方法,等后面再实现
那么问题来了,promise怎么知道衣服啥时候洗完?
Promise规定,丢给Promise执行的方法,需要将一个方法(resolve)作为参数,在执行完成以后,将返回的结果传给这个resolve方法。(有点难说清楚,上代码试试)
这样就需要改写wash方法
function wash(resolve){ console.log('开始洗衣服...'); setTimeout(()=>{ console.log('洗完了!'); resolve('一堆洗干净的衣服'); }, 2000);}
resolve('一堆干净的衣服')会调用Promise里的resolve方法,Promise就会知道洗衣服wash操作已经成功完成了,可以接下去处理then后面的事情了。把事情变得稍微复杂点试试。衣服洗完了要晾出去,晾完以后要等晒干,晒干了以后要收衣服。我们需要重写hang方法并新增dry和pickup方法
function hang(clothes){ console.log('开始晾衣服...'); /*...晾衣服中...*/ console.log(clothes+'晾好了!'); return '一堆晾好的衣服';}function dry(clothes){ console.log('等衣服干...'); /*...晾干中...*/ console.log(clothes+'晾干了!'); return '一堆晾干的衣服';}function pickup(clothes){ console.log('开始收衣服...'); /*...收衣服中...*/ console.log(clothes+'收完了!');}
对比之前的代码,发现多了一行return语句,将处理后得到的结果输出,作为参数传入接下去的要处理的方法。准备好了wash,hang,dry,pickup四个方法后,执行一下看看:
var promise = new Promise(wash);promise.then(hang).then(dry).then(pickup);
输出结果如下:
开始洗衣服...洗完了,去晾干!开始晾衣服...一堆洗干净的衣服晾完了!等衣服干...一堆晾好的衣服晾干了!开始收衣服...一堆晾干了的衣服收完了!
再稍微复杂点儿试试~上面输出结果可以看到,从’开始晾衣服...’到’一堆晾干了的衣服收完了!’几乎是同时输出的。每个动作都应该有段时间间隔才对呀~再改
function hang(clothes){ console.log('开始晾衣服...'); setTimeout(()=>{ console.log(clothes+'晾完了!'); }, 3000); return ("一堆晾好的衣服");}function dry(clothes){ console.log('等衣服干...'); setTimeout(()=>{ console.log(clothes+'晾干了!'); }, 3000); return ("一堆晾干了的衣服");}function pickup(clothes){ console.log('开始收衣服...'); setTimeout(()=>{ console.log(clothes+'收完了!'); }, 3000)}
执行一下看看……
开始洗衣服...洗完了!开始晾衣服...等衣服干...开始收衣服...一堆洗干净的衣服晾完了!一堆晾好的衣服晾干了!一堆晾干了的衣服收完了!
问题比较明显:
第一.顺序乱了
第二.从’洗完了’到’开始收衣服...’同时输出
第三.最后三句话隔了3秒后同时输出
可以看出then执行的方法并不会等setTimeout执行完才去执行接下去的then中的方法,因为then执行的方法都是同步的。咋办呢?再改~
function hang(clothes){ console.log('开始晾衣服...'); return new Promise(resolve=>{ setTimeout(()=>{ console.log(clothes+'晾完了!'); resolve("一堆晾好的衣服"); }, 3000) });}function dry(clothes){ console.log('等衣服干...'); return new Promise(resolve=>{ setTimeout(()=>{ console.log(clothes+'晾干了!'); resolve('一堆晾干了的衣服'); }, 3000) });}function pickup(clothes){ console.log('开始收衣服...'); setTimeout(()=>{ console.log(clothes+'收完了!'); }, 3000)}
hang和dry方法返回值改成了一个Promise对象。这里有点难理解,then传入的方法如果返回的是个Promise对象,那么再后面的then传入的方法就会等到这个Promise(实际上是传入Promise的方法)调用了resolve()为止,才会继续执行。
Promise原理
最头疼的部分来了,看看new Promise(wash).then(hang).then(dry).then(pickup)到底怎么实现的。
首先new Promise(wash):
实例化Promise并传入一个方法,这个方法就立刻开始执行了,所以Promise里会执行wash(resolve)方法;
wash方法中通过调用resolve(‘一堆洗干净的衣服’)通知Promise自己执行完了,所以Promise里会有一个resolve(_result_value)方法处理wash的返回结果;
再看then(hang):
then是Promise的实例方法,所以Promise里会有一个this.then = function(mission){...}实例方法。
再根据Promise的实现结果,即then后面的方法要等到wash中执行到resolve(‘一堆洗干净的衣服’)以后才能开始执行。实现方法就是在调用then(hang)的时候,不直接执行hang方法,而是把hang方法存起来,由resolve(_result_value)来触发。
初步实现
function myPromise(fn){ const missions = [];//待执行队列 var value = null; //执行传入的方法 fn(resolve); //当传入的方法中调用resolve(value)时,异步执行mission function resolve(_return_value){ value = _return_value; missions.forEach(mission=>{ mission(value); }); } //执行then方法时,将传入的方法加入missions,等待resolve触发。 this.then = function(mission){ missions.push(mission); }}
同时修改一下wash方法
function wash(resolve){ console.log('开始洗衣服...'); console.log('洗完了!'); resolve('一堆洗干净的衣服');}
执行new myPromise(wash).then(hang);输出结果:
开始洗衣服...洗完了!
晾衣服动作没执行~
来看一下发生了什么
new myPromise(wash)触发执行wash(resolve)方法=>wash(resolve)触发执行resolve(‘一堆洗干净的衣服’)=>resolve(_return_value)执行mission(value)……等下,还没执行then(hang)之前missions里还没任务呢!
所以需要改下resolve方法
function resolve(_return_value){ value = _return_value; setTimeout(()=>{ missions.forEach(mission=>{ mission(value); }) }, 0);}
在执行一下new myPromise(wash).then(hang);结果就对了。
增加状态控制
如果我们想这样使用:
var promise = new myPromise(wash);setTimeout(()=>{ promise.then(hang)}, 1000)
开始洗衣服以后干别的事情去了,过一段时间回来如果洗完了就直接晾衣服,没洗完就接着等待。
因为hang也是个异步操作,会延迟到mission(value)之后才执行,所以此时myPromise又没法正常工作了。
解决办法是给myPromise增加一个状态state。当没有触发resovle(_return_value)时,状态处在pending处理中;当触发了resovle(_return_value)时,状态置为fulfilled已处理。而this.then = function(mission){...}在处理前先对状态做个判断,pending时将mission插入missions任务队列,fulfilled时就直接执行mission(value)。
function resolve(_return_value){ state = 'fulfilled'; ...//省略其他未改动代码}this.then = function(mission){ if(state === 'fulfilled'){ mission(value); }else{ missions.push(mission); }}
任务链处理
目前执行new myPromise(wash).then(hang).then(dry).then(pickup)会报错。then方法没有设置返回值。稍微调整下代码
this.then = function(mission){ if(state === 'fulfilled'){ mission(value); }else{ missions.push(mission); } return this;}
简化hang, dry, pickup方法
function hang(clothes){ console.log('开始晾衣服...'); console.log(clothes+'晾完了!'); return ("一堆晾好的衣服");}function dry(clothes) { console.log('等衣服干...'); console.log(clothes + '晾干了!'); return ('一堆晾干了的衣服');}function pickup(clothes){ console.log('开始收衣服...'); console.log(clothes+'收完了!');}
执行new myPromise(wash).then(hang).then(dry).then(pickup),错是不报了。但是then之间没有正常传递返回的值。clothes始终是“一堆洗干净的衣服”。
myPromise中的value是在执行resolve(_return_value)时赋值的。一个myPromise对象只有一个初始任务(这里是wash),初始任务就执行了一次resovle(‘一堆洗干净的衣服’)。而所有的then方法返回的都是同一个myPromise对象,所以value指向的都是同一个值。
解决思路是,每次调用then方法后,返回一个新的myPromise对象 new myPromise(fn);在fn中执行then方法中要执行的操作。
this.then = function(mission){ function fn(resolve){ if(state === 'pending'){ missions.push(mission) }else{ const result = mission(value); resolve(result);//关键! } } return new myPromise(fn);}
当触发mission(value)时,将返回的结果作为result执行resolve(result),这就将result传递给了下一个myPromise。then直接触发mission(value)执行的操作和resolve(_result_value)是一样的,所以resolve也要调整
function resolve(_return_value){ value = _return_value; state = 'fulfilled'; setTimeout(()=>{ missions.forEach(mission=>{ const result = mission(value); resolve(result); //死循环 }) }, 0);}
执行下?死循环!resolve中应该调用的是then创建的新myPromise的resolve方法,而不是他本身。所以then方法必须把自己创建的myPromise的resolve传递出来。
var next_resolve = null;//保存then生成的下一个myPromise的resolve方法this.then = function(mission){ function fn(resolve){ next_resolve = resolve; if(state === 'pending'){ missions.push(mission) }else{ const result = mission(value); resolve(result); } } return new myPromise(fn);}function resolve(_return_value){ value = _return_value; state = 'fulfilled'; setTimeout(()=>{ missions.forEach(mission=>{ const result = mission(value); next_resolve(result); }) }, 0);}
执行下代码看看结果吧~
Promise对象传递
最后一个问题~(最后一关逻辑有点绕)
还记得之前用Promise对象来作为hang()和dry()的返回值的场景吗?hang()返回Promise对象后,Promise中resolve(‘一堆晾好的衣服’)被执行后,才会将’一堆晾好的衣服’作为参数传递给dry(clothes)方法并开始执行。
这种情况下,用前面写好的myPromise执行一下,又不对了。dry()和pickup()的形参变成了一个new myPromise,结果肯定出错嘛。
解决思路是,当遇到任务的返回值是一个object或者function,并且有自己的then方法的时候,就将它当做是一个Promise对象处理,等这个Promise对象中的方法处理到resolve(_return_result)的时候,把_return_result作为参数输出传递给后续的任务。
重写then和resolve
this.then = function(mission){ var fn = function(resolve){ next_resolve = resolve; if(state === 'pending'){ missions.push(mission) }else{ const result = mission(value); if(result && (typeof result == 'object' || typeof result == 'function')){ if(result.then && typeof result.then == 'function'){ result.then(next_resolve);//关键! } }else{ next_resolve(result); } } } return new myPromise(fn);}function resolve(_return_value){ value = _return_value; state = 'fulfilled'; setTimeout(()=>{ missions.forEach(mission=>{ const result = mission(value); if(result && (typeof result == 'object' || typeof result == 'function')){ if(result.then && typeof result.then == 'function'){ result.then(next_resolve);//关键! } }else{ next_resolve(result); } }) }, 0);}
有必要解释一下result.then(next_resolve)。result此时是一个Promise对象(取名resultPromise),我们需要在resultPromise对象方法执行到resolve(_result_value)时,获取到_result_value并传递给next_resolve(result)执行。而then方法正是干这事儿的:将next_resolve放进resultPromise的执行队列missions里,resultPromise执行resolve(_result_value),state状态变为fulfilled,触发执行next_resolve(_result_value)。后面就是next_promise状态变为fulfilled,触发执行接下去的mission...
最后把公共代码提取出来整理一下:
function myPromise(fn){ const missions = []; //待执行队列 var value = null; var state = 'pending'; var next_resolve = null; //执行传入的方法 fn(resolve); //当传入的方法中调用resolve(value)时,异步执行mission function resolve(_return_value){ value = _return_value; state = 'fulfilled'; setTimeout(()=>{ missions.forEach(mission=>{ handle(mission); }) }, 0); } //执行then方法时,将传入的方法加入missions,等待resolve触发。 this.then = function(mission){ var fn = function(resolve){ next_resolve = resolve; if(state === 'pending'){ missions.push(mission) }else{ handle(mission); } } return new myPromise(fn); } function handle(mission){ const result = mission(value); //当处理结果为Promise对象时,将next_resolve推入待执行队列 if(result && (typeof result == 'object' || typeof result == 'function')){ if(result.then && typeof result.then == 'function'){ result.then(next_resolve); } }else{ next_resolve(result); } }}
- 11、ES6 之Promise
- es6之promise被坑记
- es6之promise被坑记
- ES6之Promise对象
- ES6之Promise
- ES6之promise
- ES6规范之promise模式
- es6学习笔记之Promise
- ES6新特性之promise
- js异步之promise(ES6)
- 学习笔记:ES6之Promise
- ES6--异步操作之Promise
- ES6学习之promise对象
- 11、异步操作之Promise—ES6学习笔记
- ES6学习11(Promise)
- ES6之Promise学习之路
- ES6--Promise
- ES6 Promise
- canal 坑
- Linux 音频 API 指南
- html table属性cellspacing cellpadding
- PHP——文件(创建、写入、上传)
- leetcode88 merge sorted array
- 11、ES6 之Promise
- Audio Jack 的耳机检测和按键检测
- MATLAB中数据平滑处理
- Linux MTR命令 【转】
- 来自伯乐 大文件断点续传
- Angular2 ng2-file-upload使用FileReader在网页中显示用户选择图片
- Chisel入门教程
- shell读取文件的3中方法
- LeetCode 64. Minimum Path Sum