热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在ES6中实现Promise的完整流程

在ES6中,Promise是一种用于处理异步操作的数据结构,它代表了一个现在、将来或永远可能可用的结果。本文将详细介绍如何在ES6中实现Promise的完整流程,包括创建、链式调用、错误处理等关键步骤,帮助开发者更好地理解和应用这一重要的异步编程工具。

道貌岸然的扯淡

望文生义,promise中文意义就是许诺,也就是如今完成不了未来•••••,然则未来这玩意谁说的准呢。就像你去泡妞,你可以许下种种信誉,但能不能完成,完整取决于你此人靠不靠谱。幸亏计算机不是人,不是人,不是人,••••正因为不是人,所以它许下的许诺,它就肯定会给你一个效果。
守候许诺完成的历程当中很冗长,所以你可以做一些别的的事变,没必要总是堵在这一条道上,也就是异步。打个比方,你打电话给饭铺老板叫了个外卖,老板关照你,10分钟后送过去,也就是说老板给了你一个许诺,因而你等啊等,这中心又去上了个茅厕,玩了会手机••••••,这就是异步,老板给的许诺并没有阻碍你干别的的事变。OK,扯淡完毕。

一、promise这妞有啥好

为了完成异步,平常要设置一个回调函数

setTimeout(function(){
console.log(1);
setTimeout(function(){
console.log(2);
setTimeout(function(){
console.log(3);
setTimeout(function(){
console.log(4);
setTimeout(function(){
console.log(5);
},500)
},400)
},300)
},200)
},100);

••••••有无一种想死的觉得!
promise最大上风就是第一祛除了这类回调地狱,第二增加了毛病捕捉,像下面这类,

promis.then(function (response) {
//do something;
}, function (reason) {
//get error
}).then(function (response) {
//do something;
}, function (reason) {
//get error
}).then(function (response) {
//do something;
}, function (reason) {
//get error
});

如果不做毛病处理则更清晰

promis.then(function (response) {
//do something;
}).then(function (response) {
//do something;
}).then(function (response) {
//do something;
}).then(function (response) {
//do something;
});

它使得异步的代码看起来像是在同步实行,大大增强了代码的可读性。
美中不足的是你得写一堆的.then(function(){},function(){}),然则和回调地狱比拟,忍了。
在ES7中会有号称是异步的终究处理方案,async和await,那是后话。

二、这妞性情怎样

前面说了,计算机不是人,所以它许下的许诺,它肯定会给你一个效果,不论这个许诺的效果是吸收照样谢绝。
所以,第一,promise肯定会返回一个效果。
第二,这个效果是不可逆的,你只能吸收,实质是因为promise的状况不可逆,一旦它变成了resolve也许reject,你休想再让你变成pending,不然,它要会措辞,肯定回你的只需一个字,滚!
第三、promise的效果什么时候返回,你说了不算,你去泡妞的时候,妞也不肯定就地就准许你吧,也许想个三、五天也说不定,这个主动权不是掌握在你手中的。
第四、ES6的promise实行历程当中,你是没法获得实行的进度的,究竟它如今是pending照样resolve,照样reject。就好像妞和她的闺蜜讨论要不要吸收你,你是探询探望不到的。固然并非完整不能,比方angularjs的$q完成一个notify要领,可以获取到实行进度的关照。
末了说一点儿你的权利,你能决议的是在什么时候去取promise的效果,也就是挪用then要领的时候,就好像你天天追着妞问,你想好没有••••••,妞这个时候会有三种回复,一是准许你,二是谢绝你,三是还得再想一想,XXXX时候再关照你••••,也就说这TMD又是一个许诺•••••。
咳、咳,如今最先必需庄重点,毕竟手艺是一件庄重的事变。

三、美丽的妞,是个男子就会有主意

说白了,promise就是一个对象,一个只能经由历程then要领来获得它上面的值的对象。
在es6中,你只需大呼一句,妞,给我个许诺,它就会给你一个promise,就像下面如许:

var promise = new Promise(function(resolve,reject){
//do something;
})

然后你就可以挪用它的then要领去取值,那末从这个角度看,这个组织函数肯定是返回了一个带有then要领的对象。别的另有状况,状况的变化不可逆。再加上些别的的要领,如all、catch•••,不过不要焦急,我们一步一步来意淫出这个美丽的妞••••
1、一般状况,我们运用回调一个函数内实行别的一个函数:

function doSomething(callback){
console.log("do something");
callback();
}
doSomething(function(){
console.log("a");
});

2、然则在运用promise时,我们是用then要领去取效果,而promise就是个对象,那末上面的代码看起来应当如许写:

function doSomething(){
console.log("a");
return {
then: function(callback){
var value = 1;
callback(value);
}
}
}
doSomething().then(function(res){
console.log(res);
});

在这里,我们挪用dosomething函数时,返回了一个带有then要领的对象,然后在then要领回调中去实行,如今看来是否是有那末点模样了,时候记得两件事,对象, then要领。
3、在ES6中Promise是一个组织函数,这简朴,给这个dosomething换个名字,

function Promise(){
this.then = function(callback){
var value = 1;
callback(value);
}
}

在实例化promise的时候,要传一个函数进去,这也简朴

function Promise(fn){
this.then = function(callback){
callback();
}
}

实例化传入的函数fn中(下文中的fn都是指代这个匿名函数),你会传入2个参数,一个叫resolve,另一个叫reject,为了简朴起见,我们不斟酌reject,它的原理和resolve是一样的。那末就像如许:

var promise = new Promise(function(resolve){
var value = 1;
resolve(value);
})

即然传了一个fn函数进去,那末在实例化历程当中,这个函数肯定会在某个时候实行。实行时,它又会吸收到一个参数resolve,这个resolve肯定是一个函数,这点从上面就可以很明显的看出来,resolve在实例化时实行了,而且吸收到了一个参数,在这里是变量value。那末Promise函数内部很多是如许:

function Promise(fn){
function resolve(value){}
this.then = function (onResolved) {};
fn(resolve);
}

为了看起来更直接,这里我们把挪用then要领传的第一个函数就叫做onResolved,那末接下来我们应当斟酌在实例化的时候,另有什么事变要做,在then要领的回调函数中我们愿望获得promise的值,这个值是在fn函数挪用后被resolve函数运算后获得的,终究要在onResolved函数中拿到,也就是说,我们必需在resolve中将这个值通报给onResolved,迂回一下:

function Promise(fn) {
var callback = null;
function resolve(value) {
callback(value);
}
this.then = function(onResolved) {
callback = onResolved;
};
fn(resolve);
}

然则这里有一个题目,就是我们挪用resolve要领时,还没有挪用过then要领,因而callbak是null,浏览器报错:callback is not a function,这里hack下,让resolve要领的实行在then以后。

function Promise(fn) {
var callback = null;
function resolve(value) {
setTimeout(function(){
callback(value);
},0)
}
this.then = function(onResolved) {
callback = onResolved;
};
fn(resolve);
}

实行一下,

var promise = new Promise(function(res){
var value = 2;
res(2);
});
promise.then(function(res){
console.log(res);
})

OK,胜利的输出。目前为止,promise的表面算是被我们意淫出来了。
4、promise是有状况的,而且状况不可逆,一样的为了简朴起见,我先来搞定从pending变到resolved,那末rejected也一样。细致想下,实行了resolve要领后可以获得一个resolved状况的值,那末一定在resolve要领中会去转变promise的状况,而且获得这个值,那末代码貌似应当如许写:

function Promise(fn) {
var state = 'pending';
function resolve(newValue) {
state = 'resolved';
callback(newValue);
}
this.then = function(onResolved) {
callback = onResolved;
};
fn(resolve);
}

这里我们先把setTimeout这家伙给干掉了,因为我们到场了状况,也就意味我们是想经由历程状况的变化来晓得能不能获得值,那末题目来了,我们不晓得状况啥时候变,就像你不晓得你要泡的妞啥时候准许你一样,你只能诘问,万一妞没想好,她很可以再给你一个许诺,就是谁人活该的XXX时候再关照你,不过好歹她也算给了你一个守候的时机,而我们如今要做的就是制造这么个时机。

function Promise(fn) {
var state = 'pending';
var value;
var deferred;
function resolve(newValue) {
value = newValue;
state = 'resolved';
if(deferred) {
handle(deferred);
}
}
function handle(onResolved) {
if(state === 'pending') {
deferred = onResolved;
return;
}
onResolved(value);
}
this.then = function(onResolved) {
handle(onResolved);
};
fn(resolve);
}

这里引入了别的一个函数handle,至此可以说promise的最症结的东西我们已看到了,妞的身体逐步展现。又扯远了•••••
细致看下除了handle我们还引入两个变量value和deferred,先从最简朴的来:
value的作用很简朴,在组织函数内它是一个全局变量,起到一个桥梁作用,就是为了在handle函数内能取到newValue的值,而newValue就是fn函数里的谁人效果。
handle我们估且可以以为它是妞的一个管家,它会去替我们讯问妞有无想好,也就是去推断当前这个许诺的状况,再决议怎样做。
deferred我们估且可以如许明白,它就是管家的一个记事本,你隔三差五的去问,它老人家不得记下来,如果一不小心忘了,那就悲催了。
这下不论是同步照样异步,我们随时可以在then要领中去取值,如果值没有被resolve,也就是说状况没发生变化,deferred将给我们记录下这件事,比及resolve的谁人时候点把值传给then要领中谁人回调函数,onResolved。
在这里请默念一百遍handle,defer,再接着往下看,我保证他们会让你疑心。
5、回到最初,为何要用promise,想一想回调地狱,再想一想promise是怎样处理的,那就是then要领链式挪用。
可以完成链式挪用,也就是说then要领返回的值也肯定是个promise,如许你才.then,.then的一向写下去。空话不说,没代码说个毛:

function Promise(fn) {
var state = 'pending';
var value;
var deferred = null;
function resolve(newValue) {
value = newValue;
state = 'resolved';
if(deferred) {
handle(deferred);
}
}
function handle(handler) {
if(state === 'pending') {
deferred = handler;
return;
}
if(!handler.onResolved) {
handler.resolve(value);
return;
}
var ret = handler.onResolved(value);
handler.resolve(ret);
}
this.then = function(onResolved) {
return new Promise(function(resolve) {
handle({
onResolved: onResolved,
resolve: resolve
});
});
};
fn(resolve);
}

这下换个姿态,我们先啃硬货。我们让then要领返回了一个promise,而且这个promise实例化时传入的函数里挪用了handle函数,传入了一个对象,onResolved很显然就是then要领里第一个函数,没什么可说的。症结是这handle和resolve是哪一个?思索1分钟。
这里我们用setTimeout简朴模仿一个异步,拿一个then看下,发生了什么:

var promise = new Promise(function(resolve){
setTimeout(function(){
resolve(1);
},3000)
});
promise.then(function(res){
console.log(res);
})

起首我们去new一个promise,在实例化的历程当中,挪用了传进的谁人函数,3秒后才实行到resolve,紧接着挪用了它的then要领,这个时候因为promise的状况没变,肯定取不到值,幸亏then要领会返回个promise,因而又实行了一次promise的实例化历程。这里没法逃避的就是作用域的题目,这个关系到handle函数实行在哪一个环境中,参数的究竟从哪一个处所获取到,别的就是壮大的闭包。相干学问不诠释。
为了看的更清晰,我们到场一些标记,到chrome的掌握台中调试下:

var count = 0;
function Promise(fn) {
var state = 'pending';
var value;
var deferred = null;
var scope = ++count;
function resolve(newValue) {
value = newValue;
state = 'resolved';
console.log("resolve: I am in " +scope);
if(deferred) {
handle(deferred);
}
}
function handle(handler) {
console.log("handle: I am in " +scope);
if(state === 'pending') {
deferred = handler;
return;
}
if(!handler.onResolved) {
handler.resolve(value);
return;
}
var ret = handler.onResolved(value);
handler.resolve(ret);
}
this.then = function(onResolved) {
console.log("then: I am in " + scope);
return new Promise(function(resolve) {
console.log("then promise: I am in " + scope);
handle({
onResolved: onResolved,
resolve: resolve
});
});
};
fn(resolve);
}
var promise = new Promise(function(resolve){
setTimeout(function(){
resolve(1);
},3000)
});
promise.then(function(res){
console.log(res);
});

到场的scope是为了看管作用域的变化,以间接反应出我们挪用handle时是在哪一个作用域上查询到的,另外我们还需要看管state和deferred的变化。
主要看then挪用以后,空话不说上图:
5-1、在实行then要领的时候,scope=1,state,deferred不可用。因为模仿了异步,这个时候第一个promise的resolve要领并没有实行,这里模仿了3秒,现实状况下,比方ajax取数据时,我们并不晓得这个正确的时候,就像最先时说的,这妞啥时候准许你,主动权不在你手中,由妞说了算。
《ES6中promise怎样完成》
5-2、接下往来来往实例化then要领建立的这个promise,scope = 2,state=”pending”,deferred=null。
《ES6中promise怎样完成》
5-3、在实例化完成以后,此时去实行fn函数,scope=1,state,deferred不可用。
《ES6中promise怎样完成》
第一,函数的作用域是在定义时就天生的,而不是在挪用的时候。第二个promise定义的时候,是在第一个promise作用域上,如许纵然它被return了出去,因为闭包的特征,仍读取的是第一个作用域上值,所以这里的handle一定是第一个promise的handle。而resolve则差别,它是作为行参通报了进来,所以这里的resolve是第二个promise的resolve。
5-4、进入handle时,scope = 1,state =” pending”,deferred保留了参数。
《ES6中promise怎样完成》
5-5、3秒时候到,第一个promise里的resolve被实行了,也就是说拿到了效果,这时候候,scope=1,state = “resolved”,deferred保留着适才传进来的谁人对象,再次进入handle函数。
《ES6中promise怎样完成》
5-6、scope=1,state = “resolved”,deferred求值为true,因而肯定会继承实行。下面增加的这段代码在这里也就很清晰了,如果then要领中没有传进来的onResolved函数,这里的value将直接交给下一个then要领中的onResolved函数运用,防止一些无聊的人像如许去挪用:

promise.then().then().then(function(res){
console.log(res);
})

正常人都邑让value在onResolved函数中吸收到,然后ret就是onResolved函数的返回值,这里没有return回的值,所以ret肯定是undefined。
《ES6中promise怎样完成》
5-7、scope=2,state = “resolved”,deferred=null。这里的resolve是第个promise的resolve,所以定义的时候就是在作用域2上,如果背面再挪用then要领,天生新的promise,这时候就会将undefined作为第二个promise的值通报下去。
《ES6中promise怎样完成》
这里再次强调一下,handle要领和deferred是中心地点,其背地的精华不过照样作用域和闭包的奇妙设想。变量的读取一定先从本身所处作用域最先,如果本身作用域上读不到,才会一级一级向上接见。
6、意淫到这里基础上中心的东西就差不多了,下面我们来加上reject时的状况,直接上代码:

function Promise(fn) {
var state = 'pending';
var value;
var deferred;
this.then = function (onResolved, onRejected) {
return new Promise(function (resolve, reject) {
handle({
onResolved: onResolved,
onRejected: onRejected,
resolve: resolve,
reject: reject
});
});
};
function resolve(newValue) {
if (newValue && typeof newValue.then === 'function') {
newValue.then(resolve);
return;
}
state = 'resolved';
value = newValue;
if (deferred) {
handle(deferred);
}
}
function reject(reason) {
state = 'rejected';
value = reason;
if (deferred) {
handle(deferred);
}
}
function handle(handler) {
if (state === 'pending') {
deferred = handler;
return;
}
var handlerCallback;
if (state === 'resolved') {
handlerCallback = handler.onResolved;
} else {
handlerCallback = handler.onRejected;
}
if (!handlerCallback) {
if (state === 'resolved') {
handler.resolve(value);
} else {
handler.reject(value);
}
return;
}
var ret;
try {
ret = handlerCallback(value);
} catch (e) {
handler.reject(e);
return;
}
handler.resolve(ret);
}
fn(resolve);
}

状况基础和resolve是一样的,resolve函数中加的if推断只为了应付返回值是promise的状况下依旧可以经由历程后续的then要领取到值,handle中的try/catch块的到场使得可以捕捉到promise及then要领回调中的毛病,至于then要领的转变,看不懂的话自宫吧,你是女人当我没说。

四、别的

固然这个promise只是一个基础的完成,依旧很软弱,但基础上可以说有了一表面,剩下的部位列位看官本身增加,比方promise的all ,race,catch等。某种意义上说,它们也只是then要领的语法糖。
http://www.mattgreer.org/arti…,本文代码出处,算是进修后的一点体味,程度有限,明白不对的地方,还请斧正。


推荐阅读
  • JavaScript 中创建对象的多种方法
    本文详细介绍了 JavaScript 中创建对象的几种常见方式,包括对象字面量、构造函数和 Object.create 方法,并提供了示例代码和属性描述符的解释。 ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
  • Redux入门指南
    本文介绍Redux的基本概念和工作原理,帮助初学者理解如何使用Redux管理应用程序的状态。Redux是一个用于JavaScript应用的状态管理库,特别适用于React项目。 ... [详细]
  • This post discusses an issue encountered while using the @name annotation in documentation generation, specifically regarding nested class processing and unexpected output. ... [详细]
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文介绍了如何使用JavaScript的Fetch API与Express服务器进行交互,涵盖了GET、POST、PUT和DELETE请求的实现,并展示了如何处理JSON响应。 ... [详细]
  • 云函数与数据库API实现增删查改的对比
    本文将深入探讨使用云函数和数据库API实现数据操作(增删查改)的不同方法,通过详细的代码示例帮助读者更好地理解和掌握这些技术。文章不仅提供代码实现,还解释了每种方法的特点和适用场景。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • Symfony是一个功能强大的PHP框架,以其依赖注入(DI)特性著称。许多流行的PHP框架如Drupal和Laravel的核心组件都基于Symfony构建。本文将详细介绍Symfony的安装方法及其基本使用。 ... [详细]
  • 并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性
    Java并发编程实践目录并发编程01——ThreadLocal并发编程02——ConcurrentHashMap并发编程03——阻塞队列和生产者-消费者模式并发编程04——闭锁Co ... [详细]
  • 本文将继续探讨前端开发中常见的算法问题,重点介绍如何将多维数组转换为一维数组以及验证字符串中的括号是否成对出现。通过多种实现方法的解析,帮助开发者更好地理解和掌握这些技巧。 ... [详细]
  • 本文详细介绍如何使用 Python 集成微信支付的三种主要方式:Native 支付、APP 支付和 JSAPI 支付。每种方式适用于不同的应用场景,如 PC 网站、移动端应用和公众号内支付等。 ... [详细]
  • 本文详细解析了 offset、client 和 page 坐标系统的不同之处。offset 是相对于当前元素的边界框的距离,与滚动条无关;client 是相对于可视区域(viewport)的距离,也与滚动条无关;page 则是相对于整个文档的距离,受滚动条位置影响。 ... [详细]
  • 本文探讨了在多种编程语言中实现Hello World输出的方法,从经典的C语言到现代的JavaScript,每种语言都有其独特的表达方式。 ... [详细]
author-avatar
菲菲鱼2009
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有