摘要:首先构造函数中需要有一些状态和方法因为执行实例逻辑的时候需要这些维护好的状态和值其中着重提醒的就是的状态机是单向的且状态单向不可逆。
引言
16年时在公司分享过一次promise,犹记得当时是第一次分享,还蛮紧张的,当时分享的主要是promise的使用和基本原理,后来又给无线部门同学分享了一次。
现在回顾想想,其实讲的不是很完美,因为我当时的实现方式类似于简化版q库的实现,考虑的也不全面,也没有完全遵循promise/a+规范。经过这么长一段时间的学习和积累,阐述一下自己新的理解。
在没有promise以前,多个有依赖的异步操作一般写出来会出现嵌套,所谓的回调地域,这种写法在需要协作的项目中不方便维护,异步操作也不能直接捕获异常,需要回调中进行处理,缺点挺多的,然后就开始漫长的优化,出现了q, bluebird,jq中的defer等这些库,后来ES6标准实现了Promise,但是其链式写法还是不美观,为了代码更优雅,可以视觉上同步命令式的书写代码有了TJ大神的co再结合generator似乎完美了,但是为了优雅还要额外引入co库,成本有点大,后来ES7标准干脆直接实现了,就是所谓的async和await语法糖
Promise定义现在开始切入正题,什么是Promise呢? 简而言之promise代表承诺,专业术语就是代表一个异步操作的最终结果。
代码层面来看的话Promise是一个类,可以用来创建实例,每个实例内部封装一些方法,且维护了一些状态和值,通过使用这些状态、值和方法来将现实流程中的承诺具体代码化表示。
promise主要提供了then,catch,all,race,resolve,reject这几个方法,关于这几个方法怎么使用不在赘述,因为占据文章篇幅长,且很多其它blog重复描述过。推荐阮一峰es6入门中相关api用法解释,详细且全面。
关于具体应用的话,由于在工作中项目基于vue技术栈,所以结合axios时会使用到promise来操作异步,还有就是m站基于pwa,其中Service worker声明周期事件处理中会涉及promise,还有一些就是平时写node工具的时候会用到,用promise封装异步api操作回调,从而将异步api回调逻辑直接放到then方法中进行处理。
基于Promise/a+规范实现的代码能互相统一,虽然代码形式会有不同,但原理都差不多。
首先Promise构造函数中需要有一些状态和方法,因为执行实例then逻辑的时候需要这些维护好的状态和值,其中着重提醒的就是promise的状态机是单向的,且状态单向不可逆。
状态转变只能是 pending -> fulfilled 或者 pending -> rejected。
//构造函数初始化逻辑 let that = this; //缓存this //默认状态为pending that.status = "pending"; //此变量里放着此promise的结果 that.value = undefined; //存放的着所有成功的回调函数 that.onResolvedCallbacks = []; //存放着所有的失败的回调函数 that.onRejectedCallbacks = [];
其中内部resolve和reject逻辑如下,更改状态机状态,触发承诺逻辑执行
function resolve(value) { //更改状态 执行then注册的成功回调逻辑 if (that.status == "pending") { //解决resolve 新Promise这种情况 if(value!=null &&value.then&&typeof value.then == "function"){ return value.then(resolve,reject); } that.status = "fulfilled"; that.value = value; that.onResolvedCallbacks.forEach(item=>item(that.value)); } } function reject(reason) { //更改状态 执行then注册的失败回调逻辑或者catch中注册的失败逻辑 if (that.status == "pending") { that.status = "rejected"; that.value = reason; that.onRejectedCallbacks.forEach(item=>item(that.value)); } }
上面已经介绍了大致初始化逻辑了,下面着重介绍使用频率最高的then方法,简洁版实现如下所示
PPromise.prototype.then = function (onFulfilled, onReject) { //成功和失败的逻辑没有传递 会进行值的穿透 传递给下一个then方法 onFulfilled = isFunction(onFulfilled) ?onFulfilled:val =>val; onReject = isFunction(onReject) ?onReject:reason => {throw reason;} let self = this,promise2; switch (self.status){ case "fulfilled": promise2 = new Promise((resolve,reject) =>{ let x = onFulfilled(self.value); if(x instanceof Promise){ //递归执行then逻辑 直到内部then执行,外部promise2被resolve x.then(resolve,reject) }else{ resolve(x); } }); break case "rejected": promise2 = new Promise((resolve,reject) =>{ let x = onReject(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }) break case "pending": promise2 = new Promise((resolve,reject) =>{ self.onResolvedCallbacks.push(function(){ let x = onFulfilled(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }); self.onRejectedCallbacks.push(function(){ let x = onReject(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }); }); } return promise2; }
all方法实现
function sentry(times,cb){ let result = [],count=0; return function(i,data){ result[i] = data; if(++count==times){ cb(result); } } } Promise.all = function(promises){ return new Promise((resolve,reject) => { //利用闭包机制,目的是为了判断promises是否都执行完 let done = sentry(promises.length,resolve); for(let i=0;i{ done(i,data); },reject); } }); }
resolve实现
Promise.resolve = function(value){ return new Promise(function(resolve){ resolve(value); }); }
race实现
Promise.race = function(promises){ return new Promise((resolve,reject) =>{ for(let i=0;i自己实现的promise源码
异步优雅写法异步操作经过promisify转化成promise,在结合async实现优雅的写法
let Promise = require("bluebird"); let readFile = Promise.promisify(require("fs").readFile); async function read() { let a = await readFile("./1.txt","utf8"); let b = await readFile("./2.txt","utf8"); let c = await readFile("./3.txt","utf8"); console.log(c); return "ok"; } read().then(data => { console.log(data); });总结任何事物都不是一蹴而就的,都有一个发展过程才逐步变得完美,将自己的学习坐下记录,并加一些个人思考,如果对于本文有任何疑问或错误,欢迎斧正交流。
参考链接
https://promisesaplus.com/
https://segmentfault.com/a/11...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/92450.html
摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。写一个符合规范并可配合使用的写一个符合规范并可配合使用的理解的工作原理采用回调函数来处理异步编程。 JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过...
摘要:的翻译文档由的维护很多人说,阮老师已经有一本关于的书了入门,觉得看看这本书就足够了。前端的异步解决方案之和异步编程模式在前端开发过程中,显得越来越重要。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。 JavaScript Promise 迷你书(中文版) 超详细介绍promise的gitbook,看完再不会promise...... 本书的目的是以目前还在制定中的ECMASc...
摘要:签订协议的两方分别是异步接口和。在异步函数中,使用异常捕获的方案,代替了的异常捕获的方案。需要注意的是,在异步函数中使异步函数用时要使用,不然异步函会被同步执行。 同步与异步 通常,代码是由上往下依次执行的。如果有多个任务,就必需排队,前一个任务完成,后一个任务才会执行。这种执行模式称之为: 同步(synchronous) 。新手容易把计算机用语中的同步,和日常用语中的同步弄混淆。如,...
摘要:到这里,我已经发出了一个请求买汉堡,启动了一次交易。但是做汉堡需要时间,我不能马上得到这个汉堡,收银员给我一个收据来代替汉堡。到这里,收据就是一个承诺保证我最后能得到汉堡。 同期异步系列文章推荐谈一谈javascript异步javascript异步中的回调javascript异步之Promise.all()、Promise.race()、Promise.finally()javascr...
摘要:则是把类似的异步处理对象和处理规则进行规范化,并按照采用统一的接口来编写,而采取规定方法之外的写法都会出错。这个对象有一个方法,指定回调函数,用于在异步操作执行完后执行回调函数处理。到目前为止,已经学习了创建对象和用,方法来注册回调函数。 Promise 本文从js的异步处理出发,引入Promise的概念,并且介绍Promise对象以及其API方法。 js里的异步处理 可以参考这篇文章...
摘要:宏任务和微任务这两个是指两个队列,脚本整体代码的回调及渲染都会被加入到队列中回调浏览器实现回调都会被加入到队列。 1. macrotask (宏任务)和 microtask (微任务) 这两个是指两个队列,脚本整体代码、setTimeout、setInterval、setImmediate、I/O、的回调及UI渲染都会被加入到 macrotask 队列中, process.nextTi...
阅读 2805·2021-11-22 14:44
阅读 545·2021-11-22 12:00
阅读 3686·2019-08-30 15:54
阅读 1576·2019-08-29 17:15
阅读 1902·2019-08-29 13:50
阅读 1112·2019-08-29 13:17
阅读 3517·2019-08-29 13:05
阅读 1183·2019-08-29 11:31