异步解决方案(三)Promise
首先建议大家先看看这篇博文,这是我看过的最清晰给力的博文了:
附赠一篇笑死我了的博客,加入有一天 V8引擎重大故障,为了拯救JavaScript世界...
Promise 是ES6中最重要的特性?
确实如此。让回调更加优雅。
我的第一家公司用 jQuery,第二家公司用 async/await,第三家公司使用 jQuery 和 Promise。
比较而言,我还是最喜欢 使用 async/await,不过当时也有很多别人写的UI组件,用的是 Promise,想想,出的比ES6早,在ES6被官方认证,使用者应该还是占上风的。
1.Promise有啥方法?
看一下上面的博文,通过 consle.dir(Promise)
-> Promise 是一个构造函数。
自身拥有的方法
- all (取时间最长的)
- reject
- resolve
- race (竞赛,取时间最短的)
原型链上的方法
- catch -> async/await 中需要直接用 try .. catch 来是想,算是 ES6 优胜的一点了
- then -> 不用 callback,像 jQuery 的 always/fail/done/then
既然原型链上有方法,那么 new 出来,就能够获得这些方法
let p = new Promise((resolve, reject) => {
// 一些异步操作
setTimeout(() => {
console.log('执行完成');
resolve('将数据遗留给后人');
}, 1000);
});
可以看到,这里的 函数直接自己执行了!!! 我们只是定义了一个对象,确是自己执行。
2.Promise 如何使用?
So,我们需要在它外面封装一层函数。
const Invoke = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行完成');
resolve({name: 'Jobs'});
}, 2000);
});
};
Invoke();
通过 Invoke 函数,我们实现了 它的该出手时就出手。
接下来,体验一下 不用 callback 地薪火相传。 无限回调地狱,再见啦。
Invoke().then((data) => {
console.log(data);
});
用 callback 实现一下
const fuckPromise = (callback) => {
setTimeout(() => {
console.log('先实现一个小目标');
callback({name: 'Jobs'});
}, 1000);
};
fuckPromise((data) => {
console.log(data);
});
对比一下: 我将他们分为 前置操作 和 后续操作。
相同点
- 两者的 后续操作,都作为参数被传递
不同点
- Promise 前置操作 中返回了一个对象,并且其中有, resolve/reject 来控制数据的传递。
- Callback 前置操作 中则是 在内部调用了 方法参数。把数据完全交给 callback 来控制。
- Promise 的后续操作,使用
Promise.then()
写在里面 - Callback 的后续操作,使用
外部函数(内部函数)
当层级一多,前者就是一堆 then,而后者??? 举个例子!!
3.从 callback 到 Promise.
Callback 版本1,当回调更多的时候,我们就能 很明显地感受到 还好我没用4格缩进
let fuckCallback = () => {
setTimeout(() => {
console.log('峰哥峰哥,');
setTimeout(() => {
console.log('影魔王');
}, 1000);
}, 1000);
}
fuckCallback();
Callback 版本2: 封装一下,缩进代码确实没有那么乱了,不过数据量大的时候还是不够 直观。
let callbackOne = (callback) => {
setTimeout(() => {
console.log('峰哥峰哥');
callback();
}, 1000);
};
let callbackTwo = (callback) => {
setTimeout(() => {
console.log('峰哥牛逼');
callback();
}, 1000);
};
callbackOne(() => {
callbackTwo(() => {
console.log('也许我是下一个操作');
});
});
Promise 版本: 代码没有往后延了,是的,这就是 Promise 的作用。看起来好像没卵用?只是量不够大而已。
const stepOne = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('峰哥峰哥,');
}, 1000);
});
};
const stepTwo = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve('影魔王!');
}, 1000);
});
};
stepOne()
.then((data) => {
console.log(data);
return stepTwo();
})
.then((data) => {
console.log(data);
});
这里,我发现了一点,其实 Promise 并不关系你如何处理数据,你一开始,只需要负责,把Promise这个传承的能力执行下去就行了。
让我想起《我的英雄学院》这个动画里。 One for All 的能力,只需要传承即可。传承什么力量由当代决定。
4.Promise 的其他用法
- 直接 return 数据会怎么样?
let wrap = () => {
return new Promise(resolve => {
resolve({name: 'jobs'});
});
};
wrap()
.then(data => {
console.log(data);
return data;
})
.then(data => {
console.log('我再传', data);
});
- reject 和 catch 的用法
let testReject = () => {
return new Promise((resolve, reject) => {
const head = Math.random();
if (head > 0.5) {
reject('yyf头太大了');
} else {
resolve('千军万马避蓝猫');
}
});
}
testReject()
.then(data => console.dir(data))
.catch(e => console.error(e));
- all 的用法 (两者之中取 执行时间最长的那个 --> 同时执行,一般用来节省请求的时间)
let p1 = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log('我会执行两秒钟');
resolve();
}, 2000);
});
};
let p2 = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log('我会执行一秒钟');
resolve();
}, 1000);
});
};
Promise
.all([p1(), p2()])
.then(() => console.log('他们都 resolve了,共计时 2s'));
切记: Promise.all([arg1,arg2,...]) 是等里面 都 resolve() 才返回。
没有 resolve 参数,那是不行的。 你会发现 promise.all.then 失效了。
- race -> 竞赛,第一个 resolve 了,则进行 then 操作,第二个还是会继续执行就是了
Promise
.race([p1(), p2()])
.then(() => console.log('看看过了几秒?'));
总结:(引用了开篇的那个文章,抄那边的)
ES6 Promise的内容就这些吗?是的,能用到的基本就这些。
我怎么还见过done、finally、success、fail等,这些是啥?这些并不在Promise标准中,而是我们自己实现的语法糖
需要继续跟进一下最顶上链接的 博客。
- Promise/A+规范
- jquery中的Promise
补充一个工作中用到的 Promise,书到用时方恨少啊
需求是这样的:
1.首先这是两个请求,同时发送的请求,互相并不依赖
2.当请求A返回的是 失败、且为没有资源(404) ,则进行操作 a
3.当请求B返回的是 失败、且没有资源(404),则进行操作 b
4.当请求A、B 返回的都是 失败、且没有资源(404),则进行操作 c
用 Promise.all 实现,代码尽量漂亮(雾)
我是怎么做的呢?工作中,我并没有实现它,因为当时环境出了问题,自己的技术更是有问题,于是我放在这里 写吧。(叹气一口、唉)
1.我先去 easy-mock 创建了两个请求必须的接口
总不能老是使用 setTimeout,有种纸上谈兵的感觉.
Easy-mock 的配置,均为 get 请求.
{
"success|1": true,
"errcode": /40[2-5]/,
"message": "I'm the one."
}
{
"success|1": true,
"errcode": /40[48]/,
"message": "I'm data2"
}
2.本地采用 fetch 的异步获取方式。越来越多的浏览器支持 fetch 了。
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2');
简单地来说就是这么一句话 - 发了个请求。
让我们看看这个网站是怎么介绍的?
Ajax已死,Fetch永生
可以不用看啦,一次还是只专注一个知识点就好(==),看下 fetch 的使用结构就好了 -> 返回了一个 Promise
fetch(url).then(function(response) {
return response.json();
}).then(function(data) {
console.log(data);
}).catch(function(e) {
console.log("Oops, error");
});
万事俱备、只欠东风。东风在下面 - -
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
.then(res => res.json())
.then(data => console.log(data));
如果你按照我的教程没有成功。。。那么说明:
- 你可能需要在 easy-mock 那个官网上 稍微看看,如果那边的预览没毛病,那看下一点
- 浏览器 console 一下 fetch 看一下有没有东西, 确认下,再看下一点
- 代码写错没?老铁!!!
3.接下来,我们就可以完成我们的需求了。使用 Promise.all.想了想,还是先练习一下。
练习版本: 突然发现好简单,之前主要疑惑的地方还是在 all 方法怎么传递数据的地方。
当我明白了 all 之后的回调里,自带 resolve 里面的数据的时候,就觉得好简单了。
let arr = [404, 200];
let p1 = () => new Promise((resolve, reject) => {
setTimeout(() => {
let index = Math.round(Math.random());
let res = arr[index];
console.log('2秒后,res1:', res);
if (res === 404) {
resolve(404);
} else if (res === 200) {
resolve();
}
}, 2000);
});
let p2 = () => new Promise((resolve, reject) => {
setTimeout(() => {
let index = Math.round(Math.random());
let res = arr[index];
console.log('3秒后, res2:', res);
if (res = 404) {
resolve(404);
} else if (res === 200) {
resolve();
}
}, 3000);
});
Promise.all([p1(), p2()])
.then((data) => {
console.log(data);
})
.catch(() => {});
实战版本: 还是比较不一样的,我在这里请求了百度并且出错了,所以需要注意 resolve 不必要的判断(如果你的逻辑是这样的话,这是前提)。
let p1 = () => new Promise((resolve, reject) => {
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
.then(res => res.json())
.then(data => resolve(data && data.errcode || null))
.catch(() => resolve());
});
let p2 = () => new Promise((resolve, reject) => {
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data')
.then(res => res.json())
.then(data => resolve(data && data.errcode || null))
.catch(() => resolve());
});
Promise.all([p1(), p2()])
.then((data) => {
console.log(data);
if (data[0] === 404 || data[1] === 404) {
console.log('服务器资源不存在的逻辑');
} else {
return;
}
});
// 注意,不要在最后的 all 里再判断 catch,比较好的方式 应该是在 p1/p2 就判断好。
补充一个描述中的模仿 Promise 的结构. --- 所谓的组合构造模式,没想到在这里也遇见了。
function Promise(excutor) {
this.PromiseStatus; // 共有三种状态 pending/fulfilled/rejected(等待/完成/拒绝 状态)
this.PromiseValue; // 用于存储数据
var resolve = function(res) {};// 闭包函数
var reject = function(res) {}; // 闭包函数
executor(resolve, reject); // new 一个新的 Promise 会传递一件你需要做的事情
}
Promise.prototype.then = function() {};
Promise.prototype.catch = function() {};
Promise.prototype.chain = function() {};
上面的这个,有助于我们 理解 Promise,跟在 Chrome 打出来的东西实质上是一样的。
各种方法的 Polyfill/源码
因为自己在看 Promise.all 方法的 polyfill 的时候遇见了些困难,这些摘抄下网上关键的提示 --- Promise.all 的特点
- 接受一个 iterator 参数(可迭代的迭代器)
- 如果元素不是 Promise 格式的,则通过 Promise.resolve 转化弄成 Promise
非我族类,必存异心
- 如果全部成功,状态变为 resolve,返回值组成一个数组传递给回调
- 只要有一个失败,返回值将直接回调
其实,就我当前的了解:其实所谓的 Promise.all 就是 给外层 多加了一层 Promise
人民的统一,需要一个共同的 人/机构。 Promise 不外如此。
终于看懂了官网的 Promise.all,一些注释的记录。
let p1 = () => new Promise((resolve, reject) => {
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(e => console.log(e));
});
let p2 = () => new Promise((resolve, reject) => {
fetch('https://www.easy-mock.com/mock/5ae316f1e671403cb1da46af/example/data2')
.then(res => res.json())
.then(data => console.log(data))
.catch(e => console.log(e));
});
// polyfill start
Promise.all = function(arr) {
var args = Array.prototype.slice.call(arr);
return new Promise(function (resolve, reject) {
// 若传进来的数组为 0
if (args.length === 0) return resolve([]);
// 这里出现了计数的 数组长度 - 所有的 Promise 长度
var remaining = args.length;
function res(i, val) {
// 某种程度上,说明了 val 是一个Promise对象
if (val && (typeof val === 'object')) {
var then = val.then;
// 确认一下是不是 promise 中的 then 函数
if (typeof then === 'function') {
var p = new Promise(then.bind(val));
p.then(function(val) {
res(i, val);
}, reject);
return;
}
}
// 跳到这里来的有两种:
// A.不是Promise对象,直接跳过来
// B.执行过后的Promise对象,Promise要在上面的地方多执行一次,返回来的数据就是
args[i] = val;
if (--remaining === 0) {
// 这里就是结果数组了吧
resolve(args);
}
}
for(var i = 0; i < args.length; i++) {
res(i, args[i]);
}
})
}
Promise.all([p1(), p2()])
.then();
异步解决方案(三)Promise的更多相关文章
- 前端的异步解决方案之Promise和Await-Async
异步编程模式在前端开发过程中,显得越来越重要.从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题.随着ES6新标准的出来,处理异步数据流的解决方案又有了新的变化.Promise就是这其 ...
- 深入理解JS异步编程三(promise)
jQuery 原本写一个小动画我们可能是这样的 $('.animateEle').animate({ opacity:'.5' }, 4000,function(){ $('.animateEle2' ...
- 异步解决方案----Promise与Await
前言 异步编程模式在前端开发过程中,显得越来越重要.从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题.随着ES6新标准的到来,处理异步数据流又有了新的方案.我们都知道,在传统的aja ...
- ES6学习笔记(十二)异步解决方案Promise
1.Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了P ...
- node.js异步编程解决方案之Promise用法
node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...
- JS异步解决方案
前言 异步最早的解决方案是回调函数,如ajax,事件的回调,setInterval/setTimeout中的回调.但是回调函数有回调地狱的问题; 为了解决回调地狱的问题,社区提出了Promise解决方 ...
- 异步编程之Promise(3):拓展进阶
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- 异步编程之Promise(2):探究原理
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
- Javascript异步编程之三Promise: 像堆积木一样组织你的异步流程
这篇有点长,不过干货挺多,既分析promise的原理,也包含一些最佳实践,亮点在最后:) 还记得上一节讲回调函数的时候,第一件事就提到了异步函数不能用return返回值,其原因就是在return语句执 ...
- js async await 终极异步解决方案
既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Pr ...
随机推荐
- MD5 加密算法的使用
最近在看视频时,看到 MD5 的加密算法,感觉其在某些重要信息中,还是很好的解决了一些安全问题的.于是,就在自己理解的情况下,实现了 MD5 算法. 具体的流程大致是: (1)将指定的数据首先通过 M ...
- C++实现矩阵的相加/相称/转置/求鞍点
1.矩阵相加 两个同型矩阵做加法,就是对应的元素相加. #include<iostream> using namespace std; int main(){ int a[3][3]={{ ...
- iOS 列表三级展开
效果图如下: #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplicationDe ...
- ROS Learning-023 (提高篇-001) 准备工作 --- 安装一些必要的软件包
ROS 提高篇-001 - 准备工作 - 安装一些必要的软件 我使用的虚拟机软件:VMware Workstation 11 使用的Ubuntu系统:Ubuntu 14.04.4 LTS ROS 版本 ...
- OpenStack基础及概念
一.云计算基本概念解析 1.1什么是云计算 云计算:代表计算资源向云水循环一样,按需分配,循环利用. 1.2.云计算分类 狭义:IT基础设施的交互和使用模式,通过网络以按需,易扩展的方式 ...
- ElasticSearch 入门(转)
最大的特点: 1. 数据库的 database, 就是 index 2. 数据库的 table, 就是 tag 3. 不要使用browser, 使用curl来进行客户端操作. 否则会出现 jav ...
- Linux kdb命令
一.简介 Linux 内核调试器(KDB)允许您调试 Linux 内核.这个恰如其名的工具实质上是内核代码的补丁,它允许高手访问内核内存和数据结构.KDB 的主要优点之一就是它不需要用另一台机器进行调 ...
- setex()
设置值和有效期 $redis->setex($key, $expire, $value);//$expire,有效期,单位秒 相当于 SET key value//设置键值 EXPIRE key ...
- Ubuntu16安装GTK+2.0教程
Step 1 修改清华源(修改完可提高下载速度) 先运行 sudo gedit /etc/apt/sources.list 替换文本内容,保存,退出. # 默认注释了源码镜像以提高 apt updat ...
- Linux下面的IO模型
1. Linux下的五种I/O模型 阻塞I/O模型: 一直阻塞 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空 ...