>웹 프론트엔드 >JS 튜토리얼 >JavaScript의 Promise에 대한 자세한 소개

JavaScript의 Promise에 대한 자세한 소개

黄舟
黄舟원래의
2017-03-04 15:30:151477검색

1. 소개

JavaScript는 단일 스레드이므로 한 번에 하나의 작업만 실행할 수 있습니다. 작업 시간이 오래 걸리면 후속 작업을 기다려야 합니다. 그렇다면 이런 문제를 해결할 수 있는 방법은 없을까? (WebWorker는 제쳐두고), 이는 코드가 비동기적으로 실행되도록 하는 것입니다. 예를 들어, Ajax 비동기 요청을 수행할 때 지정된 콜백 함수의 실행을 결정하기 위해 ReadyState 값이 지속적으로 모니터링됩니다.

일반적으로 비동기 실행에는 콜백 함수, 이벤트 수신, 게시 및 구독의 세 가지 유형이 있습니다. 이벤트 수신과 게시 및 구독은 실제로 유사하지만 후자가 더 강력합니다.

콜백 함수와 마찬가지로 콜백 함수는 비동기 실행에 적용되는 가장 간단한 프로그래밍 아이디어입니다.

function async(item,callback){
    console.log(item);
    setTimeout(function(){
        callback(item+1);
    },1000);    
}

위의 예에서 async 함수가 실행되면 인쇄 작업이 완료되고 1초 후에 콜백 함수가 실행됩니다(그러나 반드시 1초일 필요는 없음, 자세한 내용은 "setTimeout things" 참조). 세부).

비동기식의 주요 목적은 논블로킹을 처리하고 성능을 향상시키는 것입니다. 작업에 다음과 같이 여러 비동기 함수 작업이 필요한 경우를 상상해 보세요.

async(1, function(item){
    async(item, function(item){
        async(item, function(item){
            console.log('To be continued..');
        });
    });
});

읽기가 좀 어렵지 않나요?

또 다른 예로, 위 코드를 더욱 강력하게 만들기 위해 예외 캡처를 추가할 수 있습니다. 비동기 모드에서는 예외 처리가 다양한 콜백 함수에 분산되어 호출 시 try...catch를 통해 예외를 처리할 수 없으므로 효과적이고 명확하게 처리하기가 어렵습니다.

야, 어떡하지?

붐붐붐붐붐붐- 약속이 데뷔합니다.

ES6 Promise를 사용하여 위 코드를 최적화하면 다음을 얻을 수 있습니다.

function opration(item){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            resolve(item+1);
        },1000);
    });
    console.log(item);
    return p;
}
function failed(e){
    console.log(e);
}
Promise.resolve(1).then(opration).then(opration).then(opration).catch(failed);

Promise를 사용하여 코드를 최적화하면 콜백 함수를 전환할 수 있다는 이점이 분명합니다. 체인 호출에 추가하고, 중첩 레이어를 피하고, 프로그램 흐름을 명확하게 만들고, catch 메서드를 통해 하나 이상의 콜백 함수에서 발생하는 오류를 균일하게 처리합니다.

아, 그렇군요. 그런데 ES6의 Promise는 누구이며 구체적인 사용 규칙은 무엇인가요? 함께 탐구해 봅시다.

2. Promise 개요

Promise는 기존 솔루션(콜백 및 이벤트)보다 합리적인 비동기 프로그래밍 솔루션입니다. 더 강력해졌습니다. ES6는 이를 언어 표준에 작성하고 사용법을 통일했으며 기본적으로 Promise 객체를 제공했습니다.

Promise 객체에는 세 가지 상태만 있습니다:

1, 보류 중: 비동기 작업이 완료되지 않았습니다.

2. 해결됨: 비동기 작업이 완료되었습니다.

3. 거부됨: 비동기 작업이 실패했습니다.

게다가 이 세 가지 상태에는 두 가지 변경 모드만 있으며, 일단 상태가 변경되면 다시 변경되지 않습니다.

보류 중인 비동기 작업.

2. 비동기 작업이 보류 상태에서 거부됨

그렇습니다. ES6 사양에 속하므로 크롬을 통해 Promise를 직접 인쇄할 수 있습니다. 이것 좀 보세요:

음, Promise가 생성자라는 것이 한눈에 분명합니다. Ook, 이를 통해 인스턴스화할 수 있습니다. 우리 자신의 Promise 객체를 만들고 이를 활용합니다.

3. Promise 시작하기 안내

Promise는 생성자이므로 먼저 새로운 것을 살펴보겠습니다.

var p = new Promise();

라고 입력하고 위 코드를 실행하면 크롬 스크린샷은 다음과 같습니다.

왜 오류가 보고되나요?

그런데 Promise 생성자는 매개변수로 함수를 필요로 하며, 매개변수로 사용되는 함수에는 두 개의 매개변수가 있습니다. 첫 번째 매개변수의 기능은 비동기 작업이 보류 중에서 해결됨으로 변경되는 경우입니다. 두 번째 매개변수는 비동기 작업이 보류 중에서 거부로 변경될 때 호출됩니다.

예를 들어, 익명 함수의 첫 번째 매개변수에 Resolve라는 이름을 지정했습니다. 데모는 다음과 같습니다:

var p = new Promise(function(resolve, reject){
    console.log('new一个Promise对象');
    setTimeout(function(){
        resolve('Monkey');
    },1000);
});

위의 코드를 실행합니다. Chrome 스크린샷은 다음과 같습니다:

특별 알림: 익명 함수를 생성자로 전달할 때 Promise 함수의 매개변수로 new를 사용하면 위 그림과 같이 익명 함수가 이미 실행된 상태입니다.

안녕하세요, 위 코드에서는 익명 함수에 setTimeout 타이머를 사용하고 1초 후에 해결을 호출합니다. 왜 정의되지 않았거나 오류가 보고되지 않습니까? !

이것이 약속의 힘입니다. 이 때문에 비동기 작업을 우아한 체인 호출로 다시 작성할 수 있습니다. 어떻게 부르나요?

"Promise 개요" 섹션에서 크롬을 통해 Promise를 인쇄하고 빨간색 선 프레임 안의 영역을 사용했던 것을 기억하시나요? 그 중 Promise 프로토타입에 then 메소드(Promise.prototype.then)가 있는데, 이 then 메소드를 통해서면 충분합니다. 다음과 같습니다:

p.then(function(value){
    console.log(value);
});

그 중 then 메소드에는 Promise의 해결 및 거부 매개변수에 해당하는 두 개의 익명 함수가 매개변수로 있습니다. 코드를 실행해 보면 다음과 같습니다.

好了,当then执行完后,如果我们想继续在其之后看,使用then方法链式调用,有两种情况,一种是直接返回非Promise对象的结果;另一种是返回Promise对象的结果。

1、返回非Promise对象的结果:紧跟着的then方法,resolve立刻执行。并可使用前一个then方法返回的结果。如下:

p.then(function(value){
    console.log(value);
    //返回非Promise对象,如我的对象
    return {
        name: 'Dorie',
        age: 18
    };
}).then(function(obj){
    console.log(obj.name);
});

执行上述完整代码,chrome截图如下:

2、返回Promise对象的结果:紧跟着的then方法,与new Promise后的then方法一样,需等待前面的异步执行完后,resolve方可被执行。如下:

p.then(function(value){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var message = value + ' V Dorie'
            resolve(message);
        },1000);
    });
    console.log(value);
    //返回一个Promise对象
    return p;
}).then(function(value){
    console.log(value);
});

执行上述完整代码,chrome截图如下:

那么,当创建、执行Promise方法中有异常报错,如何捕获呢?

Promise.prototype.catch原型方法,就是为其而设定的。它具有冒泡的特性,比如当创建Promise实例时,就出错了,错误消息就会通过链式调用的这条链,一直追溯到catch方法,如果找到尽头都没有,就报错,并且再找到catch之前的所有then方法都不能执行了。Demo如下(代码太长,请自行展开):

<!DOCTYPE html>
    <head>
        <title>test</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <script>
            var p = new Promise(function(resolve, reject){
                //M未定义
                console.log(M);
                setTimeout(function(){
                    resolve(&#39;Monkey&#39;);
                },1000);
            });
            p.then(function(value){
                var p = new Promise(function(resolve, reject){
                    setTimeout(function(){
                        var message = value + &#39; V Dorie&#39;
                        resolve(message);
                    },1000);
                });
                console.log(value);
                //返回一个Promise对象
                return p;
            }).then(function(value){
                console.log(value);
                return &#39;next is catch&#39;;
            }).catch(function(e){
                console.log(e);
            }).then(function(value){
                console.log(&#39;execute,but value is &#39; + value);
            });
        </script>
    </body>
</html>

执行上述代码,chrome截图如下:

好了,到这里,我们已经了解了最常用的Promise.prototype.then和Promise.prototype.catch这两个原型方法。另外,像Promise构造函数还有属于自身的方法,如all、rece、resolve、reject等,详情请点击这里(here)。

通过一路上对Promise的讲述,我们也有了一定的认识,其实Promise并没有想象中的那么难以理解嘛。懂得Promise概念后,其实我们自己也可以实现一个简易版的Promise。下面就一同尝试实现一个呗。

四、模拟Promise

假设:有三个异步操作方法f1,f2,f3,且f2依赖于f1,f3依赖于f2。如果,我们采用ES6中Promise链式调用的思想,我们可以将程序编写成这样:

f1().then(f2).then(f3);

那么,通过上面这一系列链式调用,怎样才能达到与ES6中Promise相似的功能呢?

初步想法:首先将上述链式调用的f2、f3保存到f1中,当f1中的异步执行完后,再调用执行f2,并将f1中的f3保存到f2中,最后,等f2中的异步执行完毕后,调用执行f3。详细构思图,如下:

从上图可知,由于f1、f2 、f3是可变得,所以存储数组队列thens,可放入,我们即将创建的模拟Promise构造函数中。具体实现代码如下:

//模拟Promise
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    }
};

并且,需要一个Promise.prototype.resolve原型方法,来实现:当f1异步执行完后,执行紧接着f1后then中的f2方法,并将后续then中方法,嫁接到f2中,如f3。具体实现代码如下:

//模拟Promise,增加resolve原型方法
function Promise(){
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function(){
        var t = this.thens.shift(), 
            p;
        if(t){
            p = t.apply(null,arguments);
            if(p instanceof Promise){
                p.thens = this.thens;
            }
        }
    }
};

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}
f1().then(f2).then(f3);

仔细品味,上述实现的Promise.prototype.resolve方法还不够完美,因为它只能够满足于f1、f2、f3等方法都是使用模拟的Promise异步执行的情况。而,当其中有不是返回的Promise对象的呢,而是返回一个数字,亦或是什么也不返回,该怎么办?所以,针对以上提出的种种可能,再次改进resolve。改善代码如下:

//模拟Promise,改善resolve原型方法
var Promise = function () {
    this.thens = [];
};
Promise.prototype = {
    constructor: Promise,
    then: function(callback){
        this.thens.push(callback);
        return this;        
    },
    resolve: function () {
        var t,p;
        t = this.thens.shift();
        t && (p = t.apply(null, arguments));
        while(t && !(p instanceof Promise)){
            t = this.thens.shift();
            t && (p = t.call(null, p));    
        }
        if(this.thens.length){
            p.thens = this.thens;
        };
    }
}

测试代码(代码太长,自行打开并运行)。

function f1() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(1);
        promise.resolve();
    }, 1500)

    return promise;
}

function f2() {
    var promise = new Promise();
    setTimeout(function () {
        console.log(2);
        promise.resolve();
    }, 1500);
    return promise;
}

function f3() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(3);
        promise.resolve();
    }, 1500)

    return promise;
}

function f4() {
    console.log(4);
    return 11;
}

function f5(x) {
    console.log(x+1);
}

function f6() {
    var promise = new Promise();
    setTimeout(function () {

        console.log(6);
        promise.resolve();
    }, 1500)

    return promise;
}

function f7() {
    console.log(7);
}

var that = f1().then(f2).then(f3).then(f4).then(f5).then(f6).then(f7);

好了,初步模拟的Promise就OK啦。

吼吼,对于Promise,我们这一路走来,发现原来也不过如此呢。

 以上就是详细介绍JavaScript 中的 Promise的内容,更多相关内容请关注PHP中文网(www.php.cn)!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.