ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript の Promise の詳細な紹介

JavaScript の Promise の詳細な紹介

黄舟
黄舟オリジナル
2017-03-04 15:30:151398ブラウズ

1. はじめに

JavaScript はシングルスレッドであり、一度に 1 つのタスクしか実行できません。タスクに時間がかかると、後続のタスクは待機する必要があります。では、このような問題を解決する方法はあるのでしょうか? (WebWorker は置いといて)、つまり、コードを非同期に実行させることです。これは何を意味しますか? たとえば、Ajax 非同期リクエストを行う場合、readyState の値が継続的に監視され、指定されたコールバック関数の実行が決定されます。

非同期実行には通常、コールバック関数、イベント リスニング、パブリッシュとサブスクライブの 3 つのタイプがあります。イベント リスニングとパブリッシュとサブスクライブは実際には似ていますが、後者の方が堅牢です。

コールバック関数と同様に、コールバック関数は非同期実行に適用される最も単純なプログラミングのアイデアです。以下の通り:

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

上記の例では、async 関数が実行されると印刷操作が完了し、1 秒後にコールバック関数が実行されます (ただし、必ずしも 1 秒である必要はありません。詳細については「setTimeout に関する事項」を参照してください)。

非同期の主な目的は、ノンブロッキングを処理し、パフォーマンスを向上させることです。次のように、操作に複数の非同期関数操作が必要な場合を想像してください:

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 の使用 最適化されたコードには、コールバック関数をチェーン呼び出しに変換し、ネストの層を回避し、プログラムを変更するという明らかな利点があります。フローは明確である必要があり、1 つ以上のコールバック関数によってスローされたエラーは catch メソッドを通じて均一に処理される必要があります。

ああ、それはいいのですが、ES6 の Promise とは誰ですか?具体的な使用ルールは何ですか?一緒に調べてみましょう。

2. Promise

Promise は、従来のソリューション (コールバックやイベント) よりも合理的かつ強力な非同期プログラミングのソリューションです。これは最初にコミュニティによって提案および実装され、ES6 がそれを言語標準に組み込み、その使用法を統一し、Promise オブジェクトをネイティブに提供しました。

Promise オブジェクトには 3 つの状態しかありません:

1、 保留中: 非同期操作は完了していません。

2、が解決されました: 非同期操作が完了しました。

3. 拒否: 非同期操作が失敗しました。

また、これら 3 つの状態には 2 つの変更モードしかなく、一度状態が変更されると、再度変更されることはありません:

1、保留中から解決済みへの非同期操作

2、からの非同期操作。拒否されるまで保留中です。

そうですね、これは ES6 仕様に属しているので、Chrome を介して Promise を直接出力して、これを見ることができます:

まあ、Promise がコンストラクター、OK、渡されました。これを使用すると、独自の Promise オブジェクトをインスタンス化し、それを使用できます。

3. Promise 入門ガイド

Promise はコンストラクターなので、最初に新しいものを見てみましょう。次のように:

var p = new Promise();

して上記のコードを実行すると、Chrome のスクリーンショットは次のようになります:

エラーが報告されるのはなぜですか?

ところで、Promise コンストラクターにはパラメーターとして関数が必要で、パラメーターとしての関数には 2 つのパラメーターがあり、非同期操作が保留中から解決済みに変更されるときに関数を提供するために使用されます。 2 番目のパラメータは、非同期操作が保留中から拒否されたときに呼び出すために使用されます。

たとえば、匿名関数の最初のパラメータにはresolveという名前を付け、2番目のパラメータにはrejectという名前を付けました。デモは次のとおりです:

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

と上記のコードを実行します。 Chrome のスクリーンショットは次のとおりです:

特別な注意事項: 匿名関数がコンストラクター Promise のパラメーターとして渡されるとき、 new では、上に示したように、匿名関数がすでに実行されています。

上記のコードでは、匿名関数で setTimeout タイマーを使用し、1 秒後にresolveを呼び出していますが、なぜ未定義またはエラーが報告されないのでしょうか? !

これが約束の力です。このため、非同期操作をエレガントなチェーン呼び出しに書き直すことができます。なんと呼びますか?

「Promise の概要」セクションで、Chrome 経由で Promise を印刷し、赤い線のボックス内の領域を使用したことを覚えていますか?このうち、Promise プロトタイプには then メソッド (Promise.prototype.then) があり、これを介せば十分です。以下の通り:

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

このうち、then メソッドはパラメータとして 2 つの匿名関数を持ち、これは Promise のsolve パラメータとreject パラメータに対応します。コードを実行すると、結果は次のようになります:

好了,当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 までご連絡ください。