Maison  >  Article  >  développement back-end  >  Explication détaillée des promesses en JavaScript/Node.JS

Explication détaillée des promesses en JavaScript/Node.JS

小云云
小云云original
2018-03-19 14:45:201395parcourir

Les objets Explication détaillée des promesses en JavaScript/Node.JS sont utilisés pour les calculs différés et asynchrones. Cet article partage principalement avec vous l'explication détaillée des promesses en JavaScript/Node.JS. J'espère qu'il pourra vous aider.

Une Promesse est dans l'un des trois états suivants :

  • en attente : état initial, non tenue ou rejetée.

  • rempli : opération réussie.

  • rejeté : opération échouée.

L'interface Explication détaillée des promesses en JavaScript/Node.JS représente un proxy pour une valeur, qui est créée dans la promesse est pas nécessairement connu. Il vous permet d'associer des gestionnaires au statut de réussite ou d'échec éventuel d'une action asynchrone. Cela permet aux méthodes asynchrones de renvoyer des valeurs tout comme les méthodes synchrones : les méthodes asynchrones renvoient une promesse qui contient une valeur à un moment donné. future. Remplacez la valeur de retour finale.

Une promesse dans l'état en attente peut devenir soit un état rempli avec une valeur, soit un état rejeté avec une raison. Lorsqu'une situation se produit, elle est alors mise en file d'attente via la promesse. méthode. ) sera appelée. (Lors de la liaison du gestionnaire correspondant, ce gestionnaire sera appelé si la promesse est déjà dans l'état rempli ou rejeté, il n'y a donc pas de temps entre l'achèvement de l'opération asynchrone et la liaison de son gestionnaire. Condition de concurrence.)

Étant donné que les méthodes Explication détaillée des promesses en JavaScript/Node.JS.prototype.then et Explication détaillée des promesses en JavaScript/Node.JS.prototype.catch renvoient des promesses, elles peuvent être chaînées – une opération appelée composition.

Explication détaillée des promesses en JavaScript/Node.JS

Pourquoi la Promesse apparaît-elle ?

Pour faire simple, Explication détaillée des promesses en JavaScript/Node.JS est considéré comme l'élixir de l'enfer du rappel.

La fonction Callback est une fonctionnalité majeure de JavaScript ! L'API officielle node.js transmet essentiellement les valeurs de retour des fonctions de manière rappelée. Ceux qui sont habitués à la programmation synchrone seront quelque peu habitués à cette méthode asynchrone, et imbriqués couche par couche, ils construiront sans le savoir une grande pyramide de rappel. En réponse à ce problème courant, Explication détaillée des promesses en JavaScript/Node.JS a vu le jour !

Utilisation de base de Explication détaillée des promesses en JavaScript/Node.JS

Créer une promesse

promise.then(function(result) {
  console.log(result); // “完美!”
}, function(err) {
  console.log(err); // Error: "出问题了"
});

"puis" accepte deux paramètres, l'un est appelé en cas de succès et l'autre est appelé en cas d'échec, les deux sont facultatif, vous ne pouvez donc gérer que les cas de réussite ou d’échec.

Pratique de promesse

  1. Traitement des valeurs

Vous pouvez apporter quelques modifications au résultat et renvoyer une nouvelle valeur :

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  console.log("Got chapter 1!", chapter1);
});

Vous renvoyez un objet "Explication détaillée des promesses en JavaScript/Node.JS-like"

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.log("Failed!", error);
});

Vous pouvez également utiliser "catch":

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
});

Référence de l'API Explication détaillée des promesses en JavaScript/Node.JS

Méthode statique

Explication détaillée des promesses en JavaScript/Node.JS.resolve(promise);
Renvoie une promesse (si et seulement si promise.constructor == Explication détaillée des promesses en JavaScript/Node.JS)

Explication détaillée des promesses en JavaScript/Node.JS.resolve(thenable);
Créez une nouvelle promesse à partir de l'objet pouvant être visualisé. Un objet thenable (de type Explication détaillée des promesses en JavaScript/Node.JS) est un objet avec une méthode "then".

Explication détaillée des promesses en JavaScript/Node.JS.resolve(obj);
Créez une promesse avec obj comme résultat positif.

Explication détaillée des promesses en JavaScript/Node.JS.reject(obj);
Créez une promesse avec obj comme résultat négatif. Pour des raisons de cohérence et de commodité de débogage (comme les traces de pile), obj doit être un objet d'instance Error.

Explication détaillée des promesses en JavaScript/Node.JS.all(array);
Créer une promesse, qui sera confirmée si et seulement si toutes les promesses passées dans le tableau sont affirmatives. Si une promesse dans le tableau est rencontrée et se termine par la négation. , renvoie un résultat négatif. Chaque élément du tableau passera d'abord par Explication détaillée des promesses en JavaScript/Node.JS.resolve, afin que le tableau puisse contenir des objets de type Explication détaillée des promesses en JavaScript/Node.JS ou d'autres objets. Les résultats positifs sont un tableau contenant les résultats positifs de chaque promesse dans le tableau transmis (dans l'ordre, les résultats négatifs sont les premiers résultats négatifs rencontrés dans le tableau transmis).

Explication détaillée des promesses en JavaScript/Node.JS.race(array);
Créez une promesse qui se termine par un positif lorsqu'un objet du tableau est positif, ou se termine par un négatif lorsqu'un objet du tableau est négatif.

Solutions actuelles pour Explication détaillée des promesses en JavaScript/Node.JS

En fait, il existe déjà des bibliothèques tierces qui implémentent les fonctions de Explication détaillée des promesses en JavaScript/Node.JS :

  • Q

  • quand

  • WinJS

  • RSVP.js

Les bibliothèques ci-dessus et les promesses natives JavaScript sont toutes conformes à une spécification commune et standardisée : Explication détaillée des promesses en JavaScript/Node.JSs/A+ a une méthode similaire appelée Deferred, mais elle n'est pas compatible avec la spécification Explication détaillée des promesses en JavaScript/Node.JSs/A+, il y aura donc quelques problèmes mineurs, alors utilisez-la. avec prudence. jQuery a également un type Explication détaillée des promesses en JavaScript/Node.JS, qui est en fait une version abrégée de Deferred, il a donc également le même problème.

Les navigateurs actuels ont (partiellement) implémenté Explication détaillée des promesses en JavaScript/Node.JS.

Chrome 32, Opera 19 et Firefox 29 et versions ultérieures prennent déjà en charge Explication détaillée des promesses en JavaScript/Node.JS par défaut. Puisqu'il fait partie du noyau de WebKit, nous avons des raisons de nous attendre à ce que la prochaine version de Safari le prenne également en charge, et IE est également en développement continu.

Pour atteindre le standard de compatibilité Explication détaillée des promesses en JavaScript/Node.JS sur ces deux navigateurs, ou pour utiliser Explication détaillée des promesses en JavaScript/Node.JS dans d'autres navigateurs et Node.js, vous pouvez jeter un œil à ce polyfill (2K après gzip).

玩node的同志们都知道,当这门语言被提出来的时候,作为自己最为骄傲的异步机制,却被PHP和Python等战团喷得不成样子的是,他们嘲笑着nodejs那蠢蠢的无限嵌套,nodejs战团只能以我们只要性能!!!来安慰自己。

众所周知,javascript作为一个单线程语言,所有工作都是阻塞的,有好多人不理解为什么说是javascript是阻塞的,怎么可以做到异步机制呢?

举一个栗子

在我们平时可以接触到的情况下,我们可以用浏览器来触发XMLHttpRequest(Ajax)来异步获取数据,setTimeout、setInterval来完成定时任务,而这并不是javascript的语言来决定这些异步操作的,而是解释Javascript的浏览器来去操作线程作多线程操作的,可以把这些方法理解为浏览器抛出的多线程API。而nodejs是基于高性能v8来实现,它也是像浏览器一样,抛出了很多操作线程的API,从而来实现异步机制。

异步的机制可以让我们更为节省系统资源,并不需要为每一个请求去像PHP,Tomcat一样新开一个线程,node内部会有处理各种任务的线程(使用Net,File System,Timers 等很多模块来操作不同的线程),把不同的异步任务分发给各个任务线程,并会弹性地为线程分配硬件,这都是来自v8的高性能,也是为什么nodejs能面对高I/O情况的根本原因。

现实


到头来我们必须面对血淋淋的现实,当我初接触node的时候,代码也是这样写的

fs.readFile(MrFileFirst,"utf8",function(err,data1){
    if(err){
        //do err thing
    }else{
        fs.readFile(MrFileSecond,"utf8",function(err,data2){
            if(err){
                //do err thing
            }else{
                mongo.find(SomeQuery,function(err,data3){
                    if(err){
                        //do err thing
                    }else{
                        //do the real thing with [data1,data2,data3]
                    }
                })            }
        })    }})

Oh,my god!好好的异步机制还是玩成了同步……而且惨不忍睹!仅仅只是想返回最后的三个数据,但是这个例子三个任务之间并没有关系嵌套,这样子强行把异步玩成同步的话,还是阻塞的代码,这段代码的工作时序大概在这样的:

和不用node并没有什么区别,完全是阻塞的。在平时我们可以碰到更多的关系层级的嵌套(下一步的操作要基于上一步的结果),这时才必须使用同步去完成任务,但是要是像上面这样写的话,我相信你会写到吐血的(我已经忘了我在代码中写过多个少if (err) {}了,因为node的底层API异步方法都是以err为第一个参数,使得上层所有异步方法都为这种模式)

进化


有人看不下去了,便自会有人站出来,我们渐渐地实现了从无到有的过程,我最开始接触的是阿里的

eventproxy:

var ep = require("eventproxy");ep.create("task1","task2","task3",function(result1,result2,result3){
    //do the real thing with [result1,result2,result3]}).fail(function(e){
    //do err thing});fs.readFile(MrFileFirst,"utf8",ep.done("task1"));fs.readFile(MrFileSecond,"utf8",ep.done("task2"));fs.readFile(MrFileThird,"utf8",ep.done("task3"));

这样,就可以实现三个文件异步进行读取,并且在三个任务都完成时进行最终的工作,时序图如下图:

三个任务几乎同时触发(除去代码的触发时间),所以左边的三个点其实可以看作是一个点,而这三个任务都去同时异步进行,在三个任务都完成的时候,来触发最后的任务。

这才是node发挥出自己优点的地方,处理时间节省了很多(如果三个任务的时间消耗都为1,则时间缩减了2/3),这才是大node.js。

eventproxy也有更多的用法,可以去其npm上看看。

async

async是国外强大的异步模块,它的功能与eventproxy相似,但是维护速度与周期特别快,毕竟是用的人多呀,但是支持国产——是一种情怀,附介绍使用async的文章
http://blog.fens.me/nodejs-async/

再进化


人总是不知足的,而刚好是这个不知足,才让我们不停地去探索想要的、更为方便的东西。而这时,便有人想让自己写的代码复用性更高,同时也不想去写那么多的callback去嵌套,这时便有了Promiss/A+规范,其是:

An open standard for sound, interoperable JavaScript promises—by implementers, for implementers.
一个健全的通用JavaScript Explication détaillée des promesses en JavaScript/Node.JS开放标准,源于开发者,并归于开发者

在ES6中也新增了原生Explication détaillée des promesses en JavaScript/Node.JS的使用,而之前Explication détaillée des promesses en JavaScript/Node.JS库有promise,Q,bluebird等,在这些库中现在已经慢慢对ES6的原生Explication détaillée des promesses en JavaScript/Node.JS作了兼容,虽然ES6现在还没有大规模投入使用过程中。

在其中最为出名的则是bluebird和Q库,我使用的是bluebird,先贴一段bluebird的使用代码感受感受

bluebird:

//CAST//MrFileOne.txt//MrFileTow.txt//MrFileThree.txt//关系嵌套任务var Explication détaillée des promesses en JavaScript/Node.JS = require("bluebird"),
    readFileAsync = Explication détaillée des promesses en JavaScript/Node.JS.promisify(require("fs").readFile);readFileAsync("MrFileOne.txt","utf8")
    .then(function(data){
        //if the data contains the path of MrFileTow
        var path = ..... //do something with data
        return readFileAsync(path,"utf8");
    })
    .then(function(data){
        //if the data contains the path of MrFileThree
        var path = ..... //do something with data
        return readFileAsync(path,"utf8");
    })
    .then(function(data){
        //get the data of MrFileThree
        //do something
    })
    .catch(function(err){
        console.log(err);
    });//无关系汇总任务Explication détaillée des promesses en JavaScript/Node.JS.all([        readFileAsync("MrFileOne.txt","utf8"),
        readFileAsync("MrFileTwo.txt","utf8"),
        readFileAsync("MrFileThree.txt","utf8")
    ])
    .then(function(datas){
        //do something with three data form our actors
    })
    .catch(function(err){
        console.log(err);
    });

有没有一下被这种写法所吸引,这就是Explication détaillée des promesses en JavaScript/Node.JS模块的魅力,它很优雅地将函数的回调写在了then里面,并为then返回一个新的Explication détaillée des promesses en JavaScript/Node.JS,以供下一次的then去回调本次返回的结果。

How

首先使用了方法:

readFileAsync = Explication détaillée des promesses en JavaScript/Node.JS.promisify(rquire("fs").readFile);

这个方法则是为复制了readFile方法并为其增添了Explication détaillée des promesses en JavaScript/Node.JS机制,而Explication détaillée des promesses en JavaScript/Node.JS机制是什么呢?那就是为其添加Explication détaillée des promesses en JavaScript/Node.JS方法和属性后,让整个方法的返回值为一个Explication détaillée des promesses en JavaScript/Node.JS对象,我们可以通过Explication détaillée des promesses en JavaScript/Node.JS来调用then方法,来去对这个Explication détaillée des promesses en JavaScript/Node.JS方法的回调进行处理。在Explication détaillée des promesses en JavaScript/Node.JS中都默认的将第一个参数err放在了后面的catch中,使得我们再也不用写那么多的if(err)了。我们可以直接通过在then方法中通过函数参数来获取这个Explication détaillée des promesses en JavaScript/Node.JS的异步数据,从而进行下一步的处理。

而在then方法后,其返回的也是一个Explication détaillée des promesses en JavaScript/Node.JS对象,我们可以在其后再次进行then来获取上一个then的数据并进行处理。当然,我们也可以人为地去决定这个then的返回参数,但是整个then方法返回的都是一个Explication détaillée des promesses en JavaScript/Node.JS对象。

readFileAsync("MrFileOne.txt","utf8")
    .then(function(data){
        if(.....){  //data isn't what we want
            Explication détaillée des promesses en JavaScript/Node.JS.reject("It's not correct data!");
        }else{
            return data;
        }
    })
    .then(function(){
        console.log("yeah! we got data!");
    })
    .catch(function(err){
        console.log(err);
    })

在上面代码中,如果获取到的data并不是我们想要的,则我们可直接调用Explication détaillée des promesses en JavaScript/Node.JS.reject抛出一个ERROR,并直接交给catch来处理错误,所以在控制台我们能得到的是“It's not correct data!”,并不会得到“yeah! we got data!”,因为抛出错误后其之后的then方法并不会跟着执行。

More

当然我们也可以自定义多个catch来捕获不同的ERROR,对其作不同的处理,就像下面的一样

var customError = new Error(SOMENUMBER,SOMEDESCRIPTION)readFileAsync("MrFileOne.txt","utf8")
    .then(function(data){
        switch(data){
            case CASE1:                Explication détaillée des promesses en JavaScript/Node.JS.reject(customError);
            case CASE2:
                Explication détaillée des promesses en JavaScript/Node.JS.reject(new SyntaxError("noooooo!"));
        }
    })
    .catch(customError,function(err){
        //do with customError
    })
    .catch(SyntaxError,function(err){
        //do with SyntaxError
    })
    .catch(function(err){
        console.log(err);
    })

而更多的使用方法,可以在bluebird on npm里学习得到,相信你看了之后会爱上Explication détaillée des promesses en JavaScript/Node.JS的。

Q

Q模块也是一个非常优秀的Explication détaillée des promesses en JavaScript/Node.JS,它的实现原理和bluebird都大同小异,都是基于Explication détaillée des promesses en JavaScript/Node.JS/A+标准来扩展的,所以使用上甚至都是差不了多少的,选择哪一个就看个人爱好了。

Explication détaillée des promesses en JavaScript/Node.JS编程思想


重点来啦,我们先来看一段普通的代码

var obj = (function(){
    var variable;
    
    return {
        get: function(){
            return variable;
        },
        set: function(v){
            variable = v;
        }
    }})();exports.get = obj.get;exports.set = obj.set;

这个代码实现的是创建了一个闭包来储存变量,那么我在外部调用这个模块时,则可以去操作这个值,即实现了一个Scope变量,并把它封装了起来。

矛盾

根据我们以前的思想,这段代码看起来很正常,但是这时侯我要加一判断进去,即在get方法调用时,如果varibale为undefined,那么我则去做一个读文件的操作,从文件中将它读出来,并反回,你会怎么实现呢?

你会发现,通过以往的思维,你是无法做到这一方法的,那么使用异步思维去想想呢,好像有点门头:

get: function(callback){
    if(varibale){
        callback(varibale);
    }else{
        fs.readFile("SomeFile","utf8",function(err,data){
            if(err){
                //do with err
                return;
            }
            
            callback(data);
        })    }}

这样……嗯咳咳,看起来似乎好像也许解决的还可以,但是你自己也会觉得,这其实糟透了,我们将原本的简单get函数更改得这么复杂。那么问题来了,谁会在使用的时候会想到这个get方法其实是一个回调的方法呢?你平时使用get时你会考虑说是这个里面有可以是回调吗?我们都是直接get()来获取它的返回值。

这就是我们自己给自己造成的矛盾和麻烦,这也是我以前曾经遇到的。

突破

那么在模块化的node里,我们怎么去实现这些不必要的麻烦呢?那就是用Explication détaillée des promesses en JavaScript/Node.JS思想去编写自己的代码,我们先试着用上面说到的bluebird来加工一下这段代码:

var Explication détaillée des promesses en JavaScript/Node.JS = require("bluebird"),
    fs = require("fs");var obj = (function(){
    var variable;
    
    return {
        get: function(){
            if(variable){
                return Explication détaillée des promesses en JavaScript/Node.JS.resolve(variable);
            }
            
            return Explication détaillée des promesses en JavaScript/Node.JS.promisify(fs.readFile)("SomeFile","utf8");
        },
        set: function(v){
            return Explication détaillée des promesses en JavaScript/Node.JS.resolve(variable = v);
        }
    }});exports.get = obj.get;exports.set = obj.set;

就是这么漂亮,使用Explication détaillée des promesses en JavaScript/Node.JS.resolve方法则是将变量转化为一个Explication détaillée des promesses en JavaScript/Node.JS对象,则是我们在外部对这个模块进行使用时,则要求我们使用Explication détaillée des promesses en JavaScript/Node.JS的思想去应用模块抛出的接口,比如:

var module = require("thisModule.js");module.get()
    .then(function(data){
        console.log(data);
                module.set("new String");
                return module.get;
    })
    .then(function(data){
        console.log(data);
    });

当我们使用Explication détaillée des promesses en JavaScript/Node.JS思想去面对每一个接口的时候,我们可以完全不用考虑这个模块的代码是怎么写的,这个方法该怎么用才是对的,到底是回调还是赋值。我们可以很直接的在其模块方法后then来解决一切问题!不用关心前面的工作到底做了什么,怎么做的,到底是异步还是同步,只要我们将整个工作流程都使用Explication détaillée des promesses en JavaScript/Node.JS来做的话,那会轻松很多,而且代码的可读性会变得更好!

简直是神器啊!

使用Explication détaillée des promesses en JavaScript/Node.JS编程思想去和node玩耍,你会相信真爱就在眼前。同时我也相信在前端模块化加速的今天,Explication détaillée des promesses en JavaScript/Node.JS编程思想必定会渗透至前端的更多角落。

有了Explication détaillée des promesses en JavaScript/Node.JS的处理,因为在前端代码中最多的异步处理就是Ajax,它们都被包装为了Explication détaillée des promesses en JavaScript/Node.JS .then的风格。那么对于一部分同步的非异步处理呢?如localStorage、setTimeout、setInterval之类的方法。在大多数情况下,博主仍然推荐使用Explication détaillée des promesses en JavaScript/Node.JS的方式包装,使得项目Service的返回接口统一。这样也便于像上例中的多个异步任务的串行、并行处理。在Angular路由中对于只设置template的情况,也是这么处理的。

对于setTimeout、setInterval在Angular中都已经为我们内置了$timeout和$interval服务,它们就是一种Explication détaillée des promesses en JavaScript/Node.JS的封装。对于localStorage呢?可以采用$q.when方法来直接包装localStorage的返回值的为Explication détaillée des promesses en JavaScript/Node.JS接口,如下所示:

    var data = $window.localStorage.getItem('data-api-key');
    return $q.when(data);

整个项目的Service层的返回值都可以被封装为统一的风格使用了,项目变得更加的一致和统一。在需要多个Service同时并行或者串行处理的时候,也变得简单了,一致的使用方式。

对于延迟任务的Explication détaillée des promesses en JavaScript/Node.JS DSL语义化封装

在前面已经提到Explication détaillée des promesses en JavaScript/Node.JS是延迟到未来执行某些特定任务,在调用时候则给消费者返回一个”承诺“,消费者线程并不会被阻塞。在消费者接受到”承诺“之后,消费者就不用再关心这些任务是如何完成的,以及督促生产者的任务执行状态等。直到任务完成后,消费者手中的这个”承诺“就被兑现了。

对于这类延迟机制,在前端的UI交互中也是极其常见的。比如模态窗口的显示,对于用户在模态窗口中的交互结果并不可提前预知的,用户是点击”ok“按钮,或者是”cancel“按钮,这是一个未来将会发生的延迟事件。对于这类场景的处理,也是Explication détaillée des promesses en JavaScript/Node.JS所擅长的领域。在Angular-UI的Bootstrap的modal的实现也是基于Explication détaillée des promesses en JavaScript/Node.JS的封装。

$modal.open({
    templateUrl: '/templates/modal.html',
    controller: 'ModalController',
    controllerAs: 'modal',
    resolve: {
    }
})
    .result
    .then(function ok(data) {
        // 用户点击ok按钮事件
    }, function cancel(){
        // 用户点击cancel按钮事件
    });

这是因为modal在open方法的返回值中给了我们一个Explication détaillée des promesses en JavaScript/Node.JS的result对象(承诺)。等到用户在模态窗口中点击了ok按钮,则Bootstrap会使用$q的defer来resolve来执行ok事件;相反,如果用户点击了cancel按钮,则会使用$q的defer来reject执行cancel事件。

这样就很好的解决了延迟触发的问题,也避免了callback的地狱。我们仍然可以进一步将其返回值语义化,以业务自有的术语命名而形成一套DSL API。

 function open(data){
    var defer = $q.defer();

    // resolve or reject defer;

    var promise = defer.promise;
    promise.ok = function(func){
        promise.then(func);
        return promise;
    };

    promise.cancel = function(func){
        promise.then(null, func);
        return promise;
    };

    return promise;
};

则我们可以如下方式来访问它:

$modal.open(item)
   .ok(function(data){
        // ok逻辑
   })
   .cancel(function(data){
       // cancel 逻辑
   });

是不是感觉更具有语义呢?在Angular中$http的返回方法success、error也是同样逻辑的封装。将success的注册函数注册为.then方法的成功回调,error的注册方法注册为then方法的失败回调。所以success和error方法只是Angular框架为我们在Explication détaillée des promesses en JavaScript/Node.JS语法之上封装的一套语法糖而已。

Angular的success、error回调的实现代码:

  promise.success = function(fn) {
    promise.then(function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  promise.error = function(fn) {
    promise.then(null, function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

相关推荐:

Javascript中的异步编程规范Explication détaillée des promesses en JavaScript/Node.JSs/A详细介绍_jquery

基于promise.js实现nodejs的promises库_node.js

有关Explication détaillée des promesses en JavaScript/Node.JSs异步问题详解_javascript技巧

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn