异步发展历程


我们知道Promise的出现是为了处理js中的异步操作。在Promise出现之前,多用回调来处理异步操作。一旦异步操作稍微复杂起来,就很容易出现传说中的回调地狱,会导致代码的可读性和可维护性降低。 例如我们要实现以下异步操作,用回调的执行方式如下:

let fs = require('fs');
fs.readFile('a.txt', (err,data) => {fs.readFile('b.txt', (err,data) => {fs.readFile('c.txt', (err,data) => {//...});});
});
复制代码

而用Promise则可以这样写:

let util = require('util');
let fs = require('fs');
let readFile = util.promisify(fs.readFile);readFile('a.txt').then(data => {return readFile('b.txt');}).then(data => {return readFile('c.txt');}).then(data => {//...});
复制代码

当然我们也可以用ES7中的async/await去写,会更简便:

let util = require('util');
let fs = require('fs');
let readFile = util.promisify(fs.readFile);
async function read() {await readFile('a.txt');await readFile('b.txt');await readFile('c.txt');// ...
}
read()
复制代码

通过对比,可以发现async/await的写法是最简洁的,基本上趋近于同步代码的写法了。不过async/await也是在promise的基础上演化的,可以看作是基于promise的语法糖。所以接下来我们就来看看如何手写一个promise.

Promise代码基本用法


let p = new Promise((resolve, reject) => {resolve(123)
})
p.then((data) => {console.log(data) // 此处可以取到resolve中的传入的参数值
}, (err) => {console.log('e', err)
})
复制代码

通过new Promise()传入一个executor参数

class Promise {constructor(executor) {this.status = 'pending' // 默认状态是等待态this.value = undefinedthis.reason = undefinedlet resolve = value => {if (this.status === 'pending') {this.value = valuethis.status = 'resolved'}}let reject = reason => {if (this.status === 'pending') {this.reason = reasonthis.status = 'rejecte'}}try  {executor(resolve, reject)} catch (e) {reject(e)}}// 会取promise的返回结果作为外层下一次then的参数// 如果返回// then方法调用后,返回的是一个新的promisethen(onFufilled, onRjected) {if (this.status === 'resolved') {onFufilled(this.value)}if (this.status === 'rejected') {onRjected(this.reason)}}
}
复制代码

以上就实现了具有最基本功能的promise啦,但这还远远不够哦,因为这时还不支持在promise中执行异步操作。如:

let p = new Promise((resolve, reject) => {// promise中的异步操作setTimeout(() => {resolve(123)
}, 1000)
})
p.then((data) => {console.log(data) // 此时取不到resolve中的参数值
}, (err) => {console.log('e', err)
})
复制代码

所以当promise是pending态时,我们可以在then方法调用时先将onFulfilled和onRjected保存起来,当状态转换为resolved态或rejected时调用。这里用到了发布订阅的思想。

constructor(executor) {// ...this.onResolvedCallbacks = [] // 存放成功的回调 发布订阅this.onRejectedCallbacks = [] // 存放失败的回调let resolve = value => {if (this.status === 'pending') {// ...this.onResolvedCallbacks.forEach(fn => fn()) // 发布}}let reject = reason => {if (this.status === 'pending') {// ...this.onRejectedCallbacks.forEach(fn => fn())}}// .
}
then(onFufilled, onRjected) {// ...if (this.status === 'pending') {this.onResolvedCallbacks.push(() => { // 订阅onFufilled(this.value)})  this.onRejectedCallbacks.push(() => { // 订阅onRjected(this.reason)})  }}
}
复制代码

至此我们的promise已经支持异步代码了。接下来我们需要实现then方法的链式调用,这里是比较难和绕的部分。then方法有如下特点:

  • then方法每次都会返回一个新的promise,如若显式返回一个promise,就将此promise作为其返回值(不能是当前调用then方法的promise),如若返回一个普通值,则需手动生成一个新的promise并将其return
  • then方法是异步调用的
  • 若then方法中出现异常,则调用失败态方法(reject)跳转到下一个then的onRejected
  • 当then()中未传回调参数时可以看作跳过当前then方法继续向下个then方法执行,即值的穿透
function resolvePromise(promise2, x, resolve, reject) {// 判断x是不是promiseif (promise2 === x) { // x为promise2,不能自己等待自己完成return reject(new TypeError('循环引用'))}if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // x是个promiselet called // 防止resolve后调用rejecttry {let then = x.then // 取x的then方法if (typeof then === 'function') { // 如果then是函数就认为是promisethen.call(x, y => { // 如果y是promise就继续递归解析promiseif (called) returncalled = trueresolvePromise(promise2, y, resolve, reject)}, err => {if (called) returncalled = truereject(err)})}} catch(e) {if (called) returncalled = truereject(e)}} else { // then是一个普通对象resolve(x)}
}then(onFufilled, onRjected) {// 解决不传onFufilled onRjected的问题 , 值的穿透onFufilled = typeof onFufilled === 'function' ? onFufilled : y => yonRjected = typeof onRjected === 'function' ? onRjected : err => { throw err }let promise2 // then需要返回一个新的promiseif (this.status === 'resolved') {promise2 = new Promise((resolve, reject) => {// x为promise则取其作为promise2的成功结果, 如果x为一个普通值,则作为promise2成功的结果console.log(promise2)setTimeout(() => {try {let x = onFufilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch(e) {reject(e)}}, 0)})//console.log(promise2)// resolvePromise(promise2, x, resolve, reject)}if (this.status === 'rejected') {promise2 = new Promise((resolve, reject) => {setTimeout(() => {try {let x = onRjected(this.reason)resolvePromise(promise2, x, resolve, reject)} catch(e) {reject(e)}}, 0)})}if (this.status === 'pending') { // 主要解决executor里异步代码promise2 = new Promise((resolve, reject) => {this.onResolvedCallbacks.push(() => { // 订阅setTimeout(() => {try {let x = onFufilled(this.value)resolvePromise(promise2, x, resolve, reject)} catch(e) {reject(e)}}, 0)})this.onRejectedCallbacks.push(() => {setTimeout(() => {try {let x = onFufilled(this.reason)resolvePromise(promise2, x, resolve, reject)} catch (e) {reject(e)}}, 0)})})}return promise2}复制代码

resolvePromise用来解析then方法的返回值

p.then(data => {return new Promise((resolve,reject)=>{//resolve传入的还是Promiseresolve(new Promise((resolve,reject)=>{resolve(2);}));});
})
复制代码

如何实现一个符合规范的Promise相关推荐

  1. 实现一个符合标准的Promise

    -- What i can't create, i don't understant 前言 实现Promise的目的是为了深入的理解Promies,以在项目中游刃有余的使用它.完整的代码见gitHub ...

  2. 实现一个完美符合Promise/A+规范的Promise

    原文在我的博客中:原文地址 如果文章对您有帮助,您的star是对我最好的鼓励- 简要介绍:Promise允许我们通过链式调用的方式来解决"回调地狱"的问题,特别是在异步过程中,通过 ...

  3. 实现一个符合 Promise/A+ 规范的 MyPromise

    Promise 实现一个符合 Promise/A+ 规范的 MyPromise,并实现 resolve.reject.all.race.defer.deferred等静态方法. MyPromise 作 ...

  4. 方法 手写promise_实现一个符合 Promise/A+规范的 Promise(typescript 版)

    (给前端大全加星标,提升前端技能) 转自:Col0ring juejin.cn/post/6886360224308035598 写在前面 没错,这又是一篇关于手写 Promise 的文章,想必大家已 ...

  5. 深入理解Promise并写一个符合Promise a+规范的Promise代码

    深入理解Promise并写一个符合Promise a+规范的Promise代码 关于Promise函数可以参考我写的这篇文章https://www.cnblogs.com/qiaohong/p/770 ...

  6. 写一个符合 Promises/A+ 规范并可配合 ES7 async/await 使用的 Promise

    原文地址 从历史的进程来看,Javascript 异步操作的基本单位已经从 callback 转换到 Promise.除了在特殊场景使用 stream,RxJs 等更加抽象和高级的异步数据获取和操作流 ...

  7. 一步步写一个符合Promise/A+规范的库

    Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果. ES6 中采用了 Promise/A+ 规范,Promise 实现之前,当然要先了解 Promise/A+ 规范,规范地 ...

  8. 一步步写一个符合Promise/A+规范的库 1

    Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果. ES6 中采用了 Promise/A+ 规范,Promise 实现之前,当然要先了解 Promise/A+ 规范,规范地 ...

  9. 手写一款符合Promise/A+规范的Promise

    手写一款符合Promise/A+规范的Promise 长篇预警!有点长,可以选择性观看.如果对Promise源码不是很清楚,还是推荐从头看,相信你认真从头看到尾,并且去实际操作了,肯定会有收获的.主要 ...

最新文章

  1. React React-Redux的安装,使用
  2. Sync 攻击原理及防范技术
  3. Entity Framework 学习高级篇2—改善EF代码的方法(下)
  4. ML:MLOps系列讲解之《端到端 ML工作流生命周期》解读
  5. SEO【总结】by 2019年5月
  6. 软件项目组织管理(四)项目集成管理
  7. 讯飞智能录音笔SR101:考研的温暖陪伴
  8. mysql char(36)_MySQL中char(36)被认为是GUID导致的BUG及解决方案
  9. PHP向右侧拉菜单,测试使用中
  10. T SQL + 正则表达式
  11. python学习------面向对象进阶
  12. autocoder自动代码生成器_Spring Boot 集成MyBatis Plus代码生成器
  13. 苹果游戏开发教程之如何使用 SpriteKit 和 GameplayKit 制作你的街机手机游戏
  14. innerHTML, innerText, outerHTML, outerText的区别
  15. Vue开发实例(04)之更换项目入口
  16. postgresql 数据库迁移
  17. 遥感影像镶嵌及实现(四)
  18. Excel VBA高级编程-微信群发消息工具
  19. 从fasta文件中筛选序列并输出
  20. 【LINUX】系统安全

热门文章

  1. 抗滑桩初始弹性系数计算_抗滑桩配筋计算.docx
  2. 全栈python_Pyodide:在浏览器端实现Python全栈科学计算
  3. Java9 jar兼容_java9新特性-6-多版本兼容jar包
  4. android开发自定义选择器,创建自定义android选择器
  5. 老式的计算机比现在的老太多了英语,老人自学英语30年成老师
  6. angular 关闭当前页_angular刷新当前页 angularjs页面不刷新的解决办法 - 电脑常识 - 服务器之家...
  7. mysql三高讲解(二):2.2 B+树的B的意义
  8. html选择第一个clss,css3如何选择第一个子元素?
  9. 我的世界服务器怎么注册密码1.9,我的世界1.9.x /1.10服务器BEST CRAFT(最佳工艺服务器)...
  10. mysql 8 nosql_MySQL8.0-NoSQL和SQL的对比及MySQL的优势