Promise原理和使用

  • Promise基本用法
  • 嵌套的Promise
  • 异步加载图片实例
  • Ajax异步操作实例
  • Promise新建后立即执行
  • promise实例作为参数
  • 调用resolve或reject不会终结 Promise的参数函数的执行
  • 模拟一个Promise的最简单实现
  • Promise接口超时模拟

Promise基本用法

Promise是一个构造函数,接受一个参数:callback,我们把要执行的异步任务放置在这个callback中,当这个Promise被实例化的时候,会立即执行这个callback。创建的Promise实例默认状态是pending,通过传入的resolvereject函数,可以将状态改成resolvedrejectedthencatch就是在这个状态发生改变的时候触发的)。
Promise对象用来生成Promise实例,传入一个函数(callback)作为参数,该函数的两个参数分别是resolvereject,它们是两个函数,由JavaScript引擎提供,不用自己部署。
其中,resolve函数在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
reject函数在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

来看一个Promise的例子:

/* Promise基本用法 */
function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done')})
}
timeout(100).then((value) => {console.log(value)
})

Promise新建后就会立即执行
上面代码中,timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果,这里的setTimeout相当于一个异步操作,过了100ms以后异步操作成功返回后调用resolve,此时done作为resolve的参数同样会传给then的第一个函数。

嵌套的Promise

let p1 = new Promise((resolve) => {setTimeout(function() {console.log(1)resolve()}, 1000)
})p1.then(() => {console.log(2)
}).then(() => {console.log(3)
}).then(() => {console.log(4)
})
// 1
// 2
// 3
// 4

then方法(和catch方法)执行后的内部都会返回一个新的Promise对象,且默认状态都是resolved,所以才能一直then下去,如果我们将p1的状态改变一下试试

let p1 = new Promise((resolve, reject) => {setTimeout(function() {console.log(1)reject()}, 1000)
})
p1.then(() => {console.log(2)
}, () => {console.log('a')
}).then(() => {console.log(3)
}).then(() => {console.log(4)
})
// 1
// a
// 3
// 4

如果我们需要执行的then返回的是失败的状态,需要手动返回一个失败的Promise对象,如果我们希望捕获这些错误怎么做呢?

let p1 = new Promise((resolve, reject) => {setTimeout(function() {console.log(1)reject()}, 1000)
})
p1.then(() => {console.log(2)
}, () => {console.log('a')
}).then(() => {console.log(3)return Promise.reject()
}).then(() => {console.log(4)
}).catch(() => {console.log('错了')
})
// 1
// a
// 3
// 错了

这样最后一个then就不会触发,因为上一个状态是rejected

异步加载图片实例

再来一个异步加载图片的例子比较接地气:

function loadImageAsync(url) {return new Promise(function(resolve, reject) {const image = new Image()image.src = urlimage.onload = function() {resolve(image)}image.onerror = function() {reject(new Error('Could not load image at ' + url));}})
}

这里例子就很直观了,使用Promise包装了一个图片加载的异步操作,如果加载成功,就调用resolve方法,否则就调用reject方法。

Ajax异步操作实例

下面是用Promise对象实现Ajax操作的例子:

const getJSON = function(url) {const promise = new Promise(function(resolve, reject){const handler = function() {if (this.readyState !== 4) {return}if (this.status === 200) {resolve(this.response)} else {reject(new Error(this.statusText))}}const client = new XMLHttpRequest()client.open("GET", url)client.onreadystatechange = handlerclient.responseType = "json"client.setRequestHeader("Accept", "application/json")client.send()})return promise
}getJSON("/posts.json").then(function(json) {console.log('Contents: ' + json)
}, function(error) {console.error('出错了', error)
})

上面代码中,getJSON是对XMLHttpRequest对象的封装,用于发出一个针对JSON数据的HTTP请求,并且返回一个Promise对象。如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数

Promise新建后立即执行

先上例子:

/* Promise新建后会立即执行 */let promise = new Promise((resolve) => {console.log("Promise")resolve()console.log("!!!")
})promise.then(function() {console.log("resolved.");
})
console.log("Hi!")// Promise
// !!!
// Hi!
// resolved

上面代码中,总结就是以下两点:

  • Promise实例新建后会立即执行
  • then方法指定的回调函数会在当前脚本所有同步任务执行完才会执行(所以这里的resolve最后才会输出)

promise实例作为参数

resolve函数的参数除了正常的值以外,还可能是另一个Promise实例:

const p1 = new Promise(function (resolve, reject) {// ...
})const p2 = new Promise(function (resolve, reject) {// ...resolve(p1)
})

即,一个异步操作的结果是返回另一个异步操作。
注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

const p1 = new Promise(function (resolve, reject) {console.log('进入p1函数')setTimeout(() => {console.log('进入p1的setTimeout')return reject(new Error('fail'))}, 3000)
})const p2 = new Promise(function (resolve, reject) {console.log('进入p2函数')setTimeout(() => {console.log('进入p2的setTimeout')return resolve(p1)}, 1000)
})p2.then(result => console.log(result)).catch(error => console.log(error))//进入p1函数
//进入p2函数
//进入p2的setTimeout
//进入p1的setTimeout
//Error: fail

前面也说过,Promise新建后就会立即执行,所以直接就会进入函数体。由于p2返回的是另一个Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。(这里需要知道JavaScript的单线程异步执行机制)

调用resolve或reject不会终结 Promise的参数函数的执行

具体什么意思呢,看下面的例子:

new Promise((resolve, reject) => {resolve(1)console.log(2)
}).then(r => {console.log(r)
})
// 2
// 1

上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolvedPromise是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
一般来说,调用resolvereject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolvereject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。

new Promise((resolve, reject) => {return resolve(1)// 后面的语句不会执行console.log(2)
})

模拟一个Promise的最简单实现

/* 模拟一个Promise的最简单实现 */
function Promise(executor){ //executor执行器let self = this;self.status = 'pending'; //等待态self.value  = undefined; // 表示当前成功的值self.reason = undefined; // 表示是失败的值function resolve(value){ // 成功的方法if(self.status === 'pending'){self.status = 'resolved';self.value = value;}}function reject(reason){ //失败的方法if(self.status === 'pending'){self.status = 'rejected';self.reason = reason;}}executor(resolve,reject);
}Promise.prototype.then = function(onFufiled,onRejected){let self = this;if(self.status === 'resolved'){onFufiled(self.value);}if(self.status === 'rejected'){onRejected(self.reason);}
}
module.exports = Promise;

拿开始的例子来说

function timeout(ms) {return new Promise((resolve, reject) => {setTimeout(resolve, ms, 'done')})
}
timeout(100).then((value) => {console.log(value)
})

100ms后调用resolve函数,resolve函数内部将状态设为了resolved了,然后在本脚本其他同步惹任务都完成后执行then方法返回的回调函数,此时将value值传给then方法的回调函数。

Promise接口超时模拟

如果一个接口访问时间超过400毫秒就算超时,用Promise如何解决
传统的解决思路设个定时器,设个全局变量data接受接口要返回的数据,如果400毫秒后定时器的全局data还未被赋值,说明接口请求超时,当然这种做法不够优雅,可以使用Promise来实现:

// 接口超时模拟1
function ajaxTimeOut(){return new Promise(function(resolve,reject){let data = null// ajax操作用setTimeout模拟setTimeout(function(){data = '真实数据来了'},388)setTimeout(function(){if(data === null){reject('接口访问超时')} else {resolve(data)}},400)})
}
ajaxTimeOut().then(function(data){console.log('success,data is' + data)
}).catch(function(err){console.log('err信息:'+err)
})

虽然看起来好像是解决了,但是怎么看怎么像愚蠢的解决方案嵌套了一层美丽的皮囊,下面结合Promiserace方法来更好的解决超时需求:

// 接口超时模拟2
function ajax(){return new Promise(function(resolve,reject){setTimeout(function(){resolve('success')},2000)})
}
function timeOut(){return new Promise(function(resolve,reject){setTimeout(function(){reject('超时')},400)})
}
Promise.race([ajax(),timeOut()])
.then(function(data){console.log(data)
})
.catch(function(err){console.log(err)
})

Promise原理和使用相关推荐

  1. 面试题promise原理

    面试题Promise原理 在Promise的内部,有一个状态管理器的存在,有三种状态:pending.fulfilled.rejected. (1) promise 对象初始化状态为 pending. ...

  2. 从零开始,手写完整的Promise原理!

    珠峰十年深度沉淀,最具诚意与深度的课程限时免费开放,带你从0到1完美诠释异步编程,并手写一个完整的promise原理!  [扫码免费参加,限200人] (名额有限,扫描上方二维码立即参加!) 历史学员 ...

  3. js中promise原理及手动基本实现_V1

    前言 这几天面试过程,有个面试官突然跟我抠上了promise的实现原理,虽然有所准备,但是没能清晰地说出其中的原理,所以有点遗憾!!!,但是事已至此,只能默默去查了相关资料深入其中了解一番.因此就有了 ...

  4. Promise原理详解及实现方式

    在异步编程中,许多操作都会放在回调函数(callback)中,有时候需要拿到上一个异步操作的返回值再做第二次请求 比如: asyncOperation(data => {// 处理 `data` ...

  5. ES6 Promise原理

    ES6 Promise原理 一.Promise是什么 二.为什么会有Promise 1.回调地狱 + 异步同步事件调用顺序带来的双重伤害 2.回调事件的分离 三.Promise的三种状态 1.reso ...

  6. promise原理与async 及 await

    promise原理与async 及 await 1.1 Promise是一个构造函数 1.2 Promise优缺点 1.3 async 及 await 1.4 相较于 Promise,async/aw ...

  7. 简述promise原理

    一. promise应用场景 1 解决回调地狱 比如我们经常可能需要异步请求一个数据之后作为下一个异步操作的入参 getData(function(a){ getMoreData(a, functio ...

  8. Promise原理及手写Promise

    原理: Promise 原理围绕以下三个问题进行解决: (有任何一步不了解的话请先往下看手写Promise,会一步步剖析原理,看完后再继续回顾这里!!) 1. 怎么实现异步? Promise内部the ...

  9. Promise原理分析

    前言 最近讨论到了 Promise,此前知道也使用过它,但是对于其原理却不甚了解. 于是翻了翻 MDN 上的文档,又找了几篇文章看了看,研究了研究. 最终,自己尝试了一番,对于其原理也有所了解. Pr ...

最新文章

  1. mysql The server quit without updating PID file异常解决办法
  2. Excel为整列设置函数
  3. 使用JMeter和Yourkit进行REST / HTTP服务的性能分析
  4. Displaying a Refresh Control for Table Views
  5. php和c语言的字符数组中,字符数组和字符串的区别,C语言字符数组和字符串区别详解...
  6. swift python javascript_最小的Swift App
  7. java程序设计自考_java程序设计自考试题
  8. 想招到优秀的程序员?这些坑一定不要踩
  9. 计算同比和环比的区别_【数据说第三期】同比和环比数据分析时,有哪些需要注意的点?...
  10. distcp用于集群中数据传输解读
  11. 24. Swap Nodes in Pairs 1
  12. win10怎么卸载Edge浏览器
  13. html制作作业提交入口,最全的提交网站入口大全
  14. CAShapeLayer把图片做成圆形效果
  15. 电子计算机管理人事资料的准则,{转帖}关于中国的一些人事制度,职称评定
  16. JavaScript:自动生成博文目录导航
  17. 微信小程序云开发数据库操作
  18. html5如何获取音频资源6,【已解决】如何从喜马拉雅的页面中获取到mp3音频文件...
  19. Java学习笔记之 Lambda表达式
  20. node.js学习总结:node.js的内置模块,模块化,npm与包 express,前后端身份认证 JWT认证机制

热门文章

  1. 电力网络安全区域概念及划分
  2. uni-app微信小程序开通流量主图解
  3. Kotlin协程 - launch原理 笔记
  4. 医药之家:31个品种将纳入集采!河南拟牵头组成省际联盟
  5. js中数组循环删除自身元素问题
  6. sersync+rsync原理及部署
  7. JavaScript-原型链理解总结
  8. 【数据可视化进阶之路】第一节:看板搭建思维框架
  9. 955 加班少的公司名单(2021年)
  10. 如何运行dist文件夹