一、先看一个应用场景

  发送一个请求获得用户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啊含糊的额改造,看这里~

《30分钟,让你彻底明白Promise原理》

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模型的更多相关文章

  1. 原生的 promise 的局限性

    本文来自:https://ekyu.moe/article/limits-of-native-promise-and-async-await/ 众所周知,Nodejs 已原生支持 Promise 和 ...

  2. 【es6】js原生的promise

    JavaScript 是单线程的,这意味着任何两句代码都不能同时运行,它们得一个接一个来.在浏览器中,JavaScript 和其他任务共享一个线程,不同的浏览器略有差异,但大体上这些和 JavaScr ...

  3. 大白话讲解Promise(二)理解Promise规范

    上一篇我们讲解了ES6中Promise的用法,但是知道了用法还远远不够,作为一名专业的前端工程师,还必须通晓原理.所以,为了补全我们关于Promise的知识树,有必要理解Promise/A+规范,理解 ...

  4. Promise原理讲解 && 实现一个Promise对象 (遵循Promise/A+规范)

    1.什么是Promise? Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一 2.对于几种常见异步编程方案 回调函数 事件监听 发布/ ...

  5. promise详解 : 实现promise(附实现代码)

    promise then 的特点 : then 函数的返回值是一个 promise, 可以继续调用 then 函数 回调函数 resolve 和 reject 的参数 value /reason, 可 ...

  6. Promise.resolve()与Promise

    //Promise.resolve()和Promise.reject()常用来生成已经被决议为失败或者成功的promise案例 //Promise.reject()简单一些,不管传给它什么值,它决议为 ...

  7. 关于ES6中Promise的应用-顺序合并Promise,并将返回结果以数组的形式输出

    1.Promise 基础知识梳理 创建一个Promise实例 const promise = new Promise(function(resolve, reject) { if (success){ ...

  8. 原生Ajax + Promise

    有原生写的ajax + promise嫁接下 ;(function(root){ var LD = function(obj){ if( obj instanceof LD ) return obj; ...

  9. js原生方法promise的实现

    一会儿就要回家过年了,再来手写一个promise吧,要不等着下班真的煎熬... <!DOCTYPE html> <html lang="en"> <h ...

随机推荐

  1. git常用命令行总结

    Git是当今最流行的版本控制工具.这几年GitHub也干掉了GoogleCode和Sourceforge,从三大代码仓库中脱颖而出,除了GitHub自身的优秀外,Git也是功不可没. 为何Git如此出 ...

  2. VMwaretools、共享文件夹、全屏

    VMware12.1  +  Ubuntu14.04   +  win10专业版  设置  共享文件夹和解决Ubuntu全屏问题. 我实在不喜欢这种敲敲打打的工作,不喜欢这种有点无聊的配置环境.我喜欢 ...

  3. scrapy爬取极客学院全部课程

    # -*- coding: utf-8 -*- # scrapy爬取极客学院全部课程 import scrapy from pyquery import PyQuery as pq from jike ...

  4. [转]scrapy中的logging

    logging模块是Python提供的自己的程序日志记录模块. 在大型软件使用过程中,出现的错误有时候很难进行重现,因此需要通过分析日志来确认错误位置,这也是写程序时要使用日志的最重要的原因. scr ...

  5. Python面向对象——重写与Super

    1本文的意义 如果给已经存在的类添加新的行为,采用继承方案 如果改变已经存在类的行为,采用重写方案 2图解继承.重写与Super 注:上面代码层层关联.super()可以用到任何方法里进行调用,本文只 ...

  6. 基于angularJS搭建的管理系统

    前言 angularJS搭建的系统,是一年前用的技术栈,有些地方比较过时,这里只是介绍实现思路 前端架构 工程目录 项目浅析 项目依赖包配置package.json { "name" ...

  7. 解析spring中的BeanFactory

    我们常把spring看作一个bean工厂或者ioc容器,它帮助我们负责对象的创建管理,以及对象间依赖关系的建立,还有其他的功能. 关于工厂的实现,一般来说与我们接触最多的就是BeanFactory和A ...

  8. C++因继承引发的隐藏与重写

    在区分隐藏和重写之前,先来理一理关于继承的东西... [继承] 继承是面向对象复用的重要手段.通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西.简单的说,继承就是指 ...

  9. JS日期格式化转换方法

    1. 将日期转换为指定的格式:比如转换成 年月日时分秒 这种格式:yyyy-MM-dd hh:mm:ss 或者 yyyy-MM-dd.当然是网上的方法,只是总结下. Date.prototype.fo ...

  10. [HNOI 2002]彩票

    Description 某地发行一套彩票.彩票上写有1到M这M个自然数.彩民可以在这M个数中任意选取N个不同的数打圈.每个彩民只能买一张彩票,不同的彩民的彩票上的选择不同. 每次抽奖将抽出两个自然数X ...