promise应用及原生实现promise模型
一、先看一个应用场景
发送一个请求获得用户id, 然后根据所获得的用户id去执行另外处理。当然这里我们完全可以使用回调,即在请求成功之后执行callback;
但是如果又添加需求呢?比如获得用户id之后,再发送请求去获取用户名,之后再获取用户其他信息。。。。这就陷入了callback-hell,而且代码很难维护。promise可以解决这样的问题。
function getUserId() {
return new Promise(function(resolve) {
//异步请求
http.get(url, function(res) {
resolve(res.id)
})
})
}
getUserId().then(function(id) {
//其他处理
})
上面的回调是不是看起来不叫顺手了。
二、Promise是什么?
它简单说就是一个容器,就是一个构造函数。里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
也可以说Promise 是异步编程的一种解决方案,其实我们经常使用$.ajax()就是一种promise的解决方案

更加详细的内容可以看阮一峰的《Promise对象》。
三、can i use
现在更多的浏览器对Promise提供了原生的支持。axios基于 Promise 的 HTTP 请求就是一个常用的应用栗子。

三、写点demo
当对概念有点燃的时候,回头看看自己写过的demo,肯定有不一样的理解,这即是博客的一个好处之一
function getNumber(){
return new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10);
if(num<=5){
resolve(num);
}
else{
reject('num的值大了');
}
}, 2000);
});
}
getNumber()
.then(function(data){
console.log('resolved');
console.dir(data);
})
.catch(function(data){
console.log('rejected');
console.dir(data);
});
封装一个函数,参数是定时器的时间参数
function sleep (time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
sleep(5000).then(function(val){
console.log(`${val}秒之后起床`);
});
四、怎么实现一个promise?
1、最简单的promise雏形
function Promise(fn){
if(fn && typeof fn !== 'function'){ throw new Error('Promise resolver is not a function') };
//回调函数数组,肯能同时有多个回调
this.callBacks = [];
//执行函数
const resolve = val =>{
//执行全部回调
setTimeout(()=>{
this.callBacks.forEach(cb=>{
cb(val)
});
},0)
};
//将resolve作为实参传入fn中,fn的异步什么时候有结果再去执行resolve
fn(resolve);
}
//注册回调函数,successCallback将会被压进Promise容器中的callBacks队列
Promise.prototype.then = function(successCallback){
this.callBacks.push(successCallback);
};
//测试用例
const promise = new Promise(function(resolve){
//模拟一个异步
setTimeout(()=>{
resolve('我是传给Promise内执行函数的参数')
},2000)
})
promise.then(function(val){
console.log(val);
});
说明:
(1)、创建promise实例时候传进的函数函数被赋予一个函数类型的参数resolve,resolve接收一个参数,代表异步但返回的结果;当异步操作成功之后,就会执行resolve方法;
(2)、调用then方法,将想要在Promise异步操作成功时执行的回调放入callBacks队列,其实也就是注册回调函数,
(3)、第一步中执行resolve方法就就是执行callbacks数组中的回调函数,依次执行;
(4)、加入延迟机制,通过setTimeout(),保证在resolve执行之前,then方法已经注册完所有的回调。
2、加入状态
pending :即将发生的状态
fulfilled : 成功状态
rejected : 失败状态
function Promise(fn){
if(fn && typeof fn !== 'function'){ throw new Error('Promise resolver is not a function') };
//回调函数数组
this.callBacks = []; //一开始的状态是发生的状态
this.status = 'pending'; //立即执行的参数,初始为null
this._val = Object.create(null); //执行函数
const resolve = val =>{
//改变状态
this.status = 'fulfill'; //立即执行的参数
this._val = val; //执行全部回调
setTimeout(()=>{
this.callBacks.forEach(cb => {
cb(val)
});
})
};
//将resolve作为实参传入fn中并执行,fn的异步什么时候有结果再去执行resolve函数
fn(resolve);
} Promise.prototype.then = function(successCallback){
if (this.status === 'pending') {
this.callBacks.push(successCallback);
}
successCallback(this._val);
}; const promise = new Promise(function(resolve){
//模拟一个异步
setTimeout(()=>{
resolve('我是传给Promise内执行函数的参数')
},2000)
}) promise.then(function(val){
console.log(val);
}); //此时promise实例执行的时候,status已经变为了‘fulfill’,在此之后调用then添加的新回调,都会立即执行。
setTimeout(() => {
promise.then(function(val) {
console.log(val);
});
}, 3000)
3、链式
如果在then函数里面再注入一个promise呢?
主要是对then函数和resolve啊含糊的额改造,看这里~
4、es6实现写法
class CutePromise {
constructor(executor) {
if (typeof executor !== 'function') {
throw new Error('Executor must be a function');
}
this.state = 'PENDING';
this.chained = [];
const resolve = res => {
if (this.state !== 'PENDING') {
return;
}
this.state = 'FULFILLED';
this.internalValue = res;
for (const { onFulfilled } of this.chained) {
onFulfilled(res);
}
};
const reject = err => {
if (this.state !== 'PENDING') {
return;
}
this.state = 'REJECTED';
this.internalValue = err;
for (const { onRejected } of this.chained) {
onRejected(err);
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
if (this.state === 'FULFILLED') {
onFulfilled(this.internalValue);
} else if (this.$state === 'REJECTED') {
onRejected(this.internalValue);
} else {
this.chained.push({ onFulfilled, onRejected });
}
}
}
//test
let p = new CutePromise(resolve => {
setTimeout(() => resolve('Hello'), 100);
});
p.then(res => console.log(res));
p = new CutePromise((resolve, reject) => {
setTimeout(() => reject(new Error('woops')), 100);
});
p.then(() => {}, err => console.log('Async error:', err.stack));
p = new CutePromise(() => { throw new Error('woops'); });
p.then(() => {}, err => console.log('Sync error:', err.stack));
五、总结
1、Promise的构造函数接收一个函数类型的参数,并且传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
2、resolve()执行时传入的参数,传到了之后调用的.then()中。
3、reject()执行时传入的参数,传到之后调用的.catch()中。在里面抛出代码函数的异常,不会卡死js.
4、调用getNumber()时候返回Promise的实例对象,这样就 支持了了链式的调用。
promise应用及原生实现promise模型的更多相关文章
- 原生的 promise 的局限性
本文来自:https://ekyu.moe/article/limits-of-native-promise-and-async-await/ 众所周知,Nodejs 已原生支持 Promise 和 ...
- 【es6】js原生的promise
JavaScript 是单线程的,这意味着任何两句代码都不能同时运行,它们得一个接一个来.在浏览器中,JavaScript 和其他任务共享一个线程,不同的浏览器略有差异,但大体上这些和 JavaScr ...
- 大白话讲解Promise(二)理解Promise规范
上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...
- Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)
1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/ ...
- promise详解 : 实现promise(附实现代码)
promise then 的特点 : then 函数的返回值是一个 promise, 可以继续调用 then 函数 回调函数 resolve 和 reject 的参数 value /reason, 可 ...
- Promise.resolve()与Promise
//Promise.resolve()和Promise.reject()常用来生成已经被决议为失败或者成功的promise案例 //Promise.reject()简单一些,不管传给它什么值,它决议为 ...
- 关于ES6中Promise的应用-顺序合并Promise,并将返回结果以数组的形式输出
1.Promise 基础知识梳理 创建一个Promise实例 const promise = new Promise(function(resolve, reject) { if (success){ ...
- 原生Ajax + Promise
有原生写的ajax + promise嫁接下 ;(function(root){ var LD = function(obj){ if( obj instanceof LD ) return obj; ...
- js原生方法promise的实现
一会儿就要回家过年了,再来手写一个promise吧,要不等着下班真的煎熬... <!DOCTYPE html> <html lang="en"> <h ...
随机推荐
- DevExpress控件的一些快捷操作
用的DevExpress控件时,有一些操作并不太方便,根据我自己需要的封装了一些控件的事件,调用的时候直接绑定控件的事件就可以了 例如: this.ComboBoxEdit.KeyDown += Ct ...
- C# 文件操作类大全
C# 文件操作类大全 时间:2015-01-31 16:04:20 阅读:1724 评论:0 收藏:0 [点我收藏+] 标签: 1.创建文件夹 //usin ...
- python基础——多态与多态性
python基础--多态与多态性 1 多态 多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承) 1. 序列类型有多种形态:字符串,列表,元组. 2. 动物有多种形态:人, ...
- zoj 3950 how many nines
https://vjudge.net/problem/ZOJ-3950 题意: 给出两个日期,计算从第一个日期开始到第二个日期,每一天的日期中的9加起来一共有多少个. 思路: 看题解补的题.首先看这题 ...
- Linux:nohub启动后台永久进程
nohup 命令运行由 Command参数和任何相关的 Arg参数指定的命令,忽略所有挂断(SIGHUP)信号.在注销后使用 nohup 命令运行后台中的程序.要运行后台中的 nohup 命令,添加 ...
- C#:多进程开发,控制进程数量
正在c#程序优化时,如果多线程效果不佳的情况下,也会使用多进程的方案,如下: System.Threading.Tasks.Task task=System.Threading.Tasks.Task. ...
- 从感知机到 SVM,再到深度学习(三)
这篇博文详细分析了前馈神经网络的内容,它对应的函数,优化过程等等. 在上一篇博文中已经完整讲述了 SVM 的思想和原理.讲到了想用一个高度非线性的曲线作为拟合曲线.比如这个曲线可以是: ...
- MySQL高可用架构之MHA 原理与实践
MHA简介 关于MHA MHA(Master HA)是一款开源的MySQL的高可用程序,它为MySQL主从复制架构提供了automating master failover 功能.MHA在监控到mas ...
- JAVA截取字符串的几种方式
在java中提供了很多字符串截取的方式.下面就来看看大致有几种. 1.split()+正则表达式来进行截取. 将正则传入split().返回的是一个字符串数组类型.不过通过这种方式截取会有很大的性能损 ...
- 洛谷 P3258 [JLOI2014]松鼠的新家(树链剖分)
题目描述松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来 ...