웹 프론트엔드 JS 튜토리얼 Javascript의 Promise를 철저하게 이해

Javascript의 Promise를 철저하게 이해

Oct 11, 2016 am 09:38 AM

ES6原生提供了 Promise 对象。

到底是何方妖怪呢?打出来看看:

Javascript의 Promise를 철저하게 이해

所谓 Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的 API,可供进一步处理。

Promise 对象有以下两个特点。

(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

废话不多说,直接上demo:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8" />
        <title>Promise 学习笔记</title>
        <script type="text/javascript">
            window.onload = function() {
                function pms1() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务1&#39;);
                            resolve(&#39;执行任务1成功&#39;);
                        }, 2000);
                    });
                }

                function pms2() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务2&#39;);
                            resolve(&#39;执行任务2成功&#39;);
                        }, 2000);
                    });
                }

                function pms3() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务3&#39;);
                            resolve(&#39;执行任务3成功&#39;);
                        }, 2000);
                    });
                }
                pms1().then(function(data) {
                        console.log(&#39;第1个回调:&#39; + data);
                        return pms2();
                    })
                    .then(function(data) {
                        console.log(&#39;第2个回调:&#39; + data);
                        return pms3();
                    })
                    .then(function(data) {
                        console.log(&#39;第3个回调:&#39; + data);
                        return &#39;还没完!该结束了吧!&#39;
                    }).then(function(data) {
                        console.log(data);
                    });
            }
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

怎么样?是不是灰常简单啊!

demo2:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <title></title>
        <script type="text/javascript">
            window.onload = function() {
                function pms1() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            var num = Math.ceil(Math.random() * 10); //生成1-10的随机数
                            if(num <= 5) {
                                resolve(num);
                            } else {
                                reject(&#39;数字太大了吧!&#39;);
                            }
                        }, 2000);
                    });
                }
                setInterval(function() {
                    pms1().then(function(data) {    //小于等于5的
                        console.log(data);
                    }, function(data) {     //大于的
                        console.log(data);
                    })
                }, 1000);
            }
        </script>
    </head>

    <body>
    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。

如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);

如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。

all的用法:

demo:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8" />
        <title>Promise 学习笔记</title>
        <script type="text/javascript">
            window.onload = function() {
                function pms1() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务1&#39;);
                            resolve(&#39;执行任务1成功&#39;);
                        }, 2000);
                    });
                }

                function pms2() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务2&#39;);
                            resolve(&#39;执行任务2成功&#39;);
                        }, 2000);
                    });
                }

                function pms3() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务3&#39;);
                            resolve(&#39;执行任务3成功&#39;);
                        }, 2000);
                    });
                }
                Promise.all([pms1(), pms2(), pms3()]).then(function(data) {
                    console.log(data);
                    console.log({}.toString.call(data));
                })
            }
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。

race的用法

all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。race的用法与all一样,我们把上面runAsync1的延时改为1秒来看一下:

<!DOCTYPE html>
<html>

    <head>
        <meta charset="utf-8" />
        <title>Promise 学习笔记</title>
        <script type="text/javascript">
            window.onload = function() {
                function pms1() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务1&#39;);
                            resolve(&#39;执行任务1成功&#39;);
                        }, 1000);
                    });
                }

                function pms2() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务2&#39;);
                            resolve(&#39;执行任务2成功&#39;);
                        }, 2000);
                    });
                }

                function pms3() {
                    return new Promise(function(resolve, reject) {
                        setTimeout(function() {
                            console.log(&#39;执行任务3&#39;);
                            resolve(&#39;执行任务3成功&#39;);
                        }, 3000);
                    });
                }
                Promise.race([pms1(), pms2(), pms3()]).then(function(data) {
                    console.log(data);   //注意上面的延时时间
                })
            }
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

看到没:只有第一个执行了回调!

在then里面的回调开始执行时,runAsync2()和runAsync3()并没有停止,仍旧再执行。于是再过1秒后,输出了他们结束的标志。

这个race有什么用呢?使用场景还是很多的,比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作。

再来看看jquery里面的$.Deferred:

jquery用$.Deferred实现了Promise规范,$.Deferred是个什么玩意呢?还是老方法,打印出来看看,先有个直观印象:

var def = $.Deferred();
console.log(def);
로그인 후 복사

输出如下:

Javascript의 Promise를 철저하게 이해

$.Deferred()返回一个对象,我们可以称之为Deferred对象,上面挂着一些熟悉的方法如:done、fail、then等。jquery就是用这个Deferred对象来注册异步操作的回调函数,修改并传递异步操作的状态。

Deferred对象的基本用法如下,为了不与ajax混淆,我们依旧举setTimeout的例子:

<!doctype html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
        <script type="text/javascript">
            $(function() {
                function runAsync() {
                    var def = $.Deferred();
                    setTimeout(function() {
                        console.log(&#39;执行完成&#39;);
                        def.resolve(&#39;随便什么数据&#39;);
                    }, 2000);
                    return def;
                }
                runAsync().then(function(data) {
                    console.log(data)
                });
            })
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사


Javascript의 Promise를 철저하게 이해

在runAsync函数中,我们首先定义了一个def对象,然后进行一个延时操作,在2秒后调用def.resolve(),最后把def作为函数的返回。调用runAsync的时候将返回def对象,然后我们就可以.then来执行回调函数。

是不是感觉和ES6的Promise很像呢?

区别在何处一看便知。由于jquery的def对象本身就有resolve方法,所以我们在创建def对象的时候并未像ES6这样传入了一个函数参数,是空的。在后面可以直接def.resolve()这样调用。

<!doctype html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
        <script type="text/javascript">
            $(function() {
                function runAsync() {
                    var def = $.Deferred();
                    setTimeout(function() {
                        console.log(&#39;执行完成&#39;);
                        def.resolve(&#39;随便什么数据&#39;);
                    }, 2000);
                    return def;
                }
                var pms=runAsync();
                pms.then(function(data) {
                    console.log(data)
                });
                pms.resolve(&#39;我穿越了!&#39;)
            })
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사


Javascript의 Promise를 철저하게 이해

这样也有一个弊端,因为执行runAsync()可以拿到def对象,而def对象上又有resolve方法,那么岂不是可以在外部就修改def的状态了?比如我把上面的代码修改如下:

现象会如何呢?并不会在2秒后输出“执行完成”,而是直接输出“我穿越了”。因为我们在异步操作执行完成之前,没等他自己resolve,就在外部给resolve了。这显然是有风险的,比如你定义的一个异步操作并指定好回调函数,有可能被别人给提前结束掉,你的回调函数也就不能执行了。

怎么办?jquery提供了一个promise方法,就在def对象上,他可以返回一个受限的Deferred对象,所谓受限就是没有resolve、reject等方法,无法从外部来改变他的状态,用法如下:

<!doctype html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
        <script type="text/javascript">
            $(function() {
                function runAsync() {
                    var def = $.Deferred();
                    setTimeout(function() {
                        console.log(&#39;执行完成&#39;);
                        def.resolve(&#39;随便什么数据&#39;);
                    }, 2000);
                    return def.promise();
                }
                var pms=runAsync();
                pms.then(function(data) {
                    console.log(data)
                });
                //pms.resolve(&#39;我穿越了!&#39;);    //这一句会报错jquery-3.1.1.min.js:2 Uncaught TypeError: pms.resolve is not a function
            })
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

then的链式调用
既然Deferred也是Promise规范的实现者,那么其他特性也必须是支持的。链式调用的用法如下:
로그인 후 복사
<!doctype html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        <script type="text/javascript" src="js/jquery-3.1.1.min.js"></script>
        <script type="text/javascript">
            $(function() {
                function runAsync() {
                    var def = $.Deferred();
                    setTimeout(function() {
                        console.log(&#39;执行完成&#39;);
                        def.resolve(&#39;随便什么数据&#39;);
                    }, 2000);
                    return def.promise();
                }
                var pms = runAsync();

                pms.then(function(data) {
                        console.log(&#39;1:&#39; + data);
                        return runAsync();
                    })
                    .then(function(data) {
                        console.log(&#39;2:&#39; + data);
                        return runAsync();
                    })
                    .then(function(data) {
                        console.log(&#39;3:&#39; + data);
                    });
                //pms.resolve(&#39;我穿越了!&#39;);    //这一句会报错jquery-3.1.1.min.js:2 Uncaught TypeError: pms.resolve is not a function
            })
        </script>
    </head>

    <body>

    </body>

</html>
로그인 후 복사

Javascript의 Promise를 철저하게 이해

done与fail

我们知道,Promise规范中,then方法接受两个参数,分别是执行完成和执行失败的回调,而jquery中进行了增强,还可以接受第三个参数,就是在pending状态时的回调,如下:

deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
로그인 후 복사

除此之外,jquery还增加了两个语法糖方法,done和fail,分别用来指定执行完成和执行失败的回调,也就是说这段代码:

d.then(function(){
    console.log(&#39;执行完成&#39;);
}, function(){
    console.log(&#39;执行失败&#39;);
});
로그인 후 복사

与这段代码是等价的:

d.done(function(){
    console.log(&#39;执行完成&#39;);
})
.fail(function(){
    console.log(&#39;执行失败&#39;);
});
로그인 후 복사

always的用法

jquery的Deferred对象上还有一个always方法,不论执行完成还是执行失败,always都会执行,有点类似ajax中的complete。不赘述了。

$.when的用法

jquery中,还有一个$.when方法来实现Promise,与ES6中的all方法功能一样,并行执行异步操作,在所有的异步操作执行完后才执行回调函数。不过$.when并没有定义在$.Deferred中,看名字就知道,$.when,它是一个单独的方法。与ES6的all的参数稍有区别,它接受的并不是数组,而是多个Deferred对象,如下:

$.when(runAsync(), runAsync2(), runAsync3())
.then(function(data1, data2, data3){
    console.log(&#39;全部执行完成&#39;);
    console.log(data1, data2, data3);
});
로그인 후 복사

jquery中没有像ES6中的race方法吗?就是以跑的快的为准的那个方法。对的,jquery中没有。

以上就是jquery中Deferred对象的常用方法了,还有一些其他的方法用的也不多,干脆就不记它了。接下来该说说ajax了。

ajax与Deferred的关系

jquery的ajax返回一个受限的Deferred对象,还记得受限的Deferred对象吧,也就是没有resolve方法和reject方法,不能从外部改变状态。想想也是,你发一个ajax请求,别人从其他地方给你取消掉了,也是受不了的。

既然是Deferred对象,那么我们上面讲到的所有特性,ajax也都是可以用的。比如链式调用,连续发送多个请求:

req1 = function(){
    return $.ajax(/*...*/);
}
req2 = function(){
    return $.ajax(/*...*/);
}
req3 = function(){
    return $.ajax(/*...*/);
}
 
req1().then(req2).then(req3).done(function(){
    console.log(&#39;请求发送完毕&#39;);
});
로그인 후 복사

明白了ajax返回对象的实质,那我们用起来就得心应手了。

success、error与complete

这三个方法或许是我们用的最多的,使用起来是这样的:

$.ajax(/*...*/)
.success(function(){/*...*/})
.error(function(){/*...*/})
.complete(function(){/*...*/})
로그인 후 복사

分别表示ajax请求成功、失败、结束的回调。这三个方法与Deferred又是什么关系呢?其实就是语法糖,success对应done,error对应fail,complete对应always,就这样,只是为了与ajax的参数名字上保持一致而已,更方便大家记忆,看一眼源码:

deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
로그인 후 복사

complete那一行那么写,是为了减少重复代码,其实就是把done和fail又调用一次,与always中的代码一样。deferred.promise( jqXHR )这句也能看出,ajax返回的是受限的Deferred对象。

 

jquery加了这么些个语法糖,虽然上手门槛更低了,但是却造成了一定程度的混淆。一些人虽然这么写了很久,却一直不知道其中的原理,在面试的时候只能答出一些皮毛,这是很不好的。这也是我写这篇文章的缘由。

 看一个promise.js库:

/*!
 * Promise 자바스크립트 라이브러리 v2.0.0
 */
;
(함수(창) {
    var _promise = 함수(then) {
        this.then = 그다음 || [];
        this.state = "";

        this._상수 = {
            모두: "아무거나",
            번호: "번호",
            해결됨: "해결됨",
            거부됨: "거부됨",
            보류 중: '보류 중'
        };
    };

    _promise.prototype = {
        해결: function() {
            if(this.state == this._CONSTANT.pending) {
                this.state = this._CONSTANT.resolved;
                반품;
            }
            if(this.state !== "") return;
            if(this.promiseArr) {
                for(var i = 0, j = this.promiseArr.length; i  1) {
                        반품;
                    }
                }
            }            this.state = this._CONSTANT.resolved;
            if(!this.thens) return;
            if(this.thens[0] && this.thens[0].finallyCB) this.thens[0].finallyCB.apply(null, arguments);
            var t, n;
            while(t = this.thens.shift()) {
                if(typeof t === this._CONSTANT.number) {
                    var self = 이것;
                    setTimeout(함수() {
                        var prms = new _promise(self.thens);
                        prms.resolve();
                    }, t);
                    부서지다;
                }
                var doneFn = t.done,
                    액션 = t.action;
                if(!doneFn) 계속;
                if(doneFn 인스턴스배열) {
                    var arr = [];
                    for(var i = 0, j = doneFn.length; i 
로그인 후 복사
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

JavaScript로 문자열 문자를 교체하십시오 JavaScript로 문자열 문자를 교체하십시오 Mar 11, 2025 am 12:07 AM

JavaScript 문자열 교체 방법 및 FAQ에 대한 자세한 설명 이 기사는 JavaScript에서 문자열 문자를 대체하는 두 가지 방법 인 내부 JavaScript 코드와 웹 페이지의 내부 HTML을 탐색합니다. JavaScript 코드 내부의 문자열을 교체하십시오 가장 직접적인 방법은 대체 () 메소드를 사용하는 것입니다. str = str.replace ( "find", "replace"); 이 메소드는 첫 번째 일치 만 대체합니다. 모든 경기를 교체하려면 정규 표현식을 사용하고 전역 플래그 g를 추가하십시오. str = str.replace (/fi

사용자 정의 Google 검색 API 설정 자습서 사용자 정의 Google 검색 API 설정 자습서 Mar 04, 2025 am 01:06 AM

이 튜토리얼은 사용자 정의 Google 검색 API를 블로그 또는 웹 사이트에 통합하는 방법을 보여 주며 표준 WordPress 테마 검색 기능보다보다 세련된 검색 경험을 제공합니다. 놀랍게도 쉽습니다! 검색을 Y로 제한 할 수 있습니다

자신의 Ajax 웹 응용 프로그램을 구축하십시오 자신의 Ajax 웹 응용 프로그램을 구축하십시오 Mar 09, 2025 am 12:11 AM

그래서 여기 당신은 Ajax라는이 일에 대해 배울 준비가되어 있습니다. 그러나 정확히 무엇입니까? Ajax라는 용어는 역동적이고 대화식 웹 컨텐츠를 만드는 데 사용되는 느슨한 기술 그룹을 나타냅니다. 원래 Jesse J에 의해 만들어진 Ajax라는 용어

예제 색상 JSON 파일 예제 색상 JSON 파일 Mar 03, 2025 am 12:35 AM

이 기사 시리즈는 2017 년 중반에 최신 정보와 새로운 예제로 다시 작성되었습니다. 이 JSON 예에서는 JSON 형식을 사용하여 파일에 간단한 값을 저장하는 방법을 살펴 봅니다. 키 값 쌍 표기법을 사용하여 모든 종류를 저장할 수 있습니다.

10 JQuery Syntax Highlighter 10 JQuery Syntax Highlighter Mar 02, 2025 am 12:32 AM

코드 프레젠테이션 향상 : 개발자를위한 10 개의 구문 하이 라이터 웹 사이트 나 블로그에서 코드 스 니펫을 공유하는 것은 개발자에게 일반적인 관행입니다. 올바른 구문 형광펜을 선택하면 가독성과 시각적 매력을 크게 향상시킬 수 있습니다. 티

8 멋진 jQuery 페이지 레이아웃 플러그인 8 멋진 jQuery 페이지 레이아웃 플러그인 Mar 06, 2025 am 12:48 AM

손쉬운 웹 페이지 레이아웃에 대한 jQuery 활용 : 8 에센셜 플러그인 jQuery는 웹 페이지 레이아웃을 크게 단순화합니다. 이 기사는 프로세스를 간소화하는 8 개의 강력한 JQuery 플러그인을 강조합니다. 특히 수동 웹 사이트 생성에 유용합니다.

10 JavaScript & JQuery MVC 자습서 10 JavaScript & JQuery MVC 자습서 Mar 02, 2025 am 01:16 AM

이 기사는 JavaScript 및 JQuery Model-View-Controller (MVC) 프레임 워크에 대한 10 개가 넘는 튜토리얼을 선별 한 것으로 새해에 웹 개발 기술을 향상시키는 데 적합합니다. 이 튜토리얼은 Foundatio의 다양한 주제를 다룹니다

' this ' 자바 스크립트로? ' this ' 자바 스크립트로? Mar 04, 2025 am 01:15 AM

핵심 포인트 JavaScript에서는 일반적으로 메소드를 "소유"하는 객체를 말하지만 함수가 호출되는 방식에 따라 다릅니다. 현재 객체가 없으면 글로벌 객체를 나타냅니다. 웹 브라우저에서는 창으로 표시됩니다. 함수를 호출 할 때 이것은 전역 객체를 유지하지만 객체 생성자 또는 그 메소드를 호출 할 때는 객체의 인스턴스를 나타냅니다. call (), apply () 및 bind ()와 같은 메소드를 사용 하여이 컨텍스트를 변경할 수 있습니다. 이 방법은 주어진이 값과 매개 변수를 사용하여 함수를 호출합니다. JavaScript는 훌륭한 프로그래밍 언어입니다. 몇 년 전,이 문장은있었습니다

See all articles