热门标签 | 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…,本文代码出处,算是进修后的一点体味,程度有限,明白不对的地方,还请斧正。


推荐阅读
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社区 版权所有