资讯专栏INFORMATION COLUMN

学习promise编写和使用

aaron / 1420人阅读

摘要:实现是中的一种异步编程实现方式,中异步编程主要是指浏览器事件处理,等,通过传入回调函数来实现控制反转。对象符合编程规范,目的是为异步编程提供统一接口,它最大的优点就是避免了回调金字塔。

promise实现

Promise是Javascript中的一种异步编程实现方式,js中异步编程主要是指浏览器DOM事件处理,setTimeout/setInterval,ajax等,通过传入回调函数来实现控制反转。Promsie对象符合CommonJS编程规范,目的是为异步编程提供统一接口,它最大的优点就是避免了回调金字塔。
假设要实现一个用户展示的任务,任务分为三步:

获取用户信息

获取用户图片

弹窗提示
一般之前使用方式为:

之前已经对getUserInfo()getUserImage(),showTip()方法进行了定义

getUserInfo(id, function(info){
    getUserImage(info.img, function(){
        showTip();
    })
})

如果回调函数不止3个,那将会是一个非常长的回调,换成Promise实现:

//getUserInfo返回promise
getUserInfo(id)
    .then(getUserImage)
    .then(showTip)
    .catch(function(e){
        console.log(e);
    });

简单的讲,Promsie的思想就是每一个异步任务返回一个promise对象,该对象有一个then方法,允许指定回调函数。这里getUserInfo的回调函数就是getUserImage

getUserInfo函数要进行如下改写(这里用jQuery的Deferred()实现)

function getUserInfo(id){
    var dfd = $.Deferred();
    setTimeout(function(){
        //获取用户信息
        dfd.resolve();
    }, 500);

    return dfd.promise;
}

这样一种写法回调函数就成了链式写法,程序的流程非常清楚,可以实现多个回调。
先在ES6出来之后,许多浏览器已经支持Promise方法,

promise有三种状态:

pending:初始状态

fulfilled:成功操作

rejected:失败操作
开始的时候promise是pending状态,fulfill之后就会执行回调then的回调函数,如果是reject就会调用catch进行异常处理,Promise.prototype.then() Promise.prototype.catch() 两种方法状态都会返回一个promise对象,用于后续链式调用。

建立一个promise例子
    
    
  "use strict";
var promiseCount = 0;

function testPromise(){
    var thisPromiseCount = promiseCount++;
    var log = document.getElementById("log");
    log.insertAdjacentHTML("beforeend", thisPromiseCount + 
        ") 开始 (异步调用开始)
"); //新建一个promsie对象 var p1 = new Promise(function(resolve, reject){ log.insertAdjacentHTML("beforeend", thisPromiseCount + ") Promise 开始执行 (异步调用开始)
"); window.setTimeout(function(){ resolve(thisPromiseCount); }, 2000) }); //定义promise执行fulfilled执行的then()回调函数以及执行reject后的catch回调函数 p1.then( function(val) { log.insertAdjacentHTML("beforeend", val + ") Promise fulfilled (异步代码中断)
"); }) .catch( function(reason) { console.log("Handle rejected promise ("+reason+") here."); }); log.insertAdjacentHTML("beforeend", thisPromiseCount + ") 建立promise"); }if ("Promise" in window) { var btn = document.getElementById("btn"); btn.addEventListener("click",testPromise); } else { log = document.getElementById("log"); log.innerHTML = "浏览器不支持promise接口"; } }

单击按钮后,首先执行promise

3秒之后resolve调用then()方法

实现XMLHttpRequest获取数据

使用ajax异步加载图片

  function imgLoad(url) {
    //返回promsie对象
    return new Promise(function(resolve, reject) {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url);
      xhr.responseType = "blob";
      xhr.onload = function() {
        if (xhr.status === 200) {
          resolve(xhr.response);
        } else {
          reject(Error("图像加载失败; 错误原因:" + xhr.statusText));
        }
      };
      xhr.onerror = function() {
          reject(Error("加载错误."));
      };
      xhr.send();
    });
  }
  
  var body = document.querySelector("body");
  var myImage = new Image();
  var url = "http://7qnb7a.com1.z0.glb.clouddn.com/6608717992840918931.jpg";

  imgLoad(url).then(function(response) {
     var imageURL = window.URL.createObjectURL(response);
     myImage.src = imageURL;
     body.appendChild(myImage);
  }, function(Error) {
    console.log(Error);
  });

构建一个简单的promise模式框架

首先需要建立一个对象来存储promise

Promise = function(){
    this.queue = [];
    this.value = null;
    this.status = "pending";
};

定义获取队列,设定状态和获取状态原型方法

Promsie.prototype.getQueue = function(){
    return this.queue;
};
Promise.prototype.getStatus = function(){
    return this.status;
};
Promise.prototype.setStatus = function(s, value){
    if(s === "fulfilled" || s === "rejected"){
        this.status = s;
        this.value = value || null;
        this.queue = [];
        var freezeObject = Object.freeze || function(){};
        freezeObject(this);
    }else{
        throw new Error({message:"doesn"t support status: "+s});
    }
};

定义then方法,接受两个参数用于完成和拒绝任务操作。

Promise.prototype.then = function(onFulfilled, onRejected){
    var handler = {
        "fulfilled": onFulfilled,
        "rejected": onRejected
    };
    handler.deferred = new Deferred();
    if(!this.isPending()){
        utils.procedure(this.status, handler, this.value);
    }else{
        this.queue.push(handler);
    }
    return handler.deferred.promise;
};

定义三个不同状态函数

Promise.prototype.isFulfilled = function(){
    return this.status === "fulfilled";
};
Promise.prototype.isRejected = function(){
    return this.status === "rejected";
};
Promise.prototype.isPending = function(){
    return this.status === "pending";
};

工具处理函数

var utils = (function(){
    var makeSignaler = function(deferred, type){
        return function(result){
            transition(deferred, type, result);
        }
    };
    
    var procedure = function(type, handler, result){
        var func = handler[type];
        var def = handler.deferred;
        if(func){
            try{
                var newResult = func(result);
                if(newResult && typeof newResult.then === "function"){
                    newResult.then(makeSignaler(def, "fulfilled"), makeSignaler(def, "rejected"));
                }else{transition(def, type, newResult);}
            }catch(err){transition(def, "rejected", err);}
        }else{transition(def, type, result);}
    };

    var transition = function(deferred, type, result){
        if(type === "fulfilled"){
            deferred.resolve(result);
        }else if(type === "rejected"){
            deferred.reject(result);
        }else if(type !== "pending"){
            throw new Error({"messgae":"doesn,t support type:"+type});
        } 
    };

    return {
        "procedure": procedure
    }
})();

定义延迟函数

Deferred = function() {
    this.promise = new Promise();
};

Deferred.prototype.resolve = function(result) {
    if (!this.promise.isPending()) {
        return;
    }

    var queue = this.promise.getQueue();
    for (var i = 0, len = queue.length; i < len; i++) {
        utils.procedure("fulfilled", queue[i], result);
    }
    this.promise.setStatus("fulfilled", result);
};

Deferred.prototype.reject = function(err) {
    if (!this.promise.isPending()) {
        return;
    }

    var queue = this.promise.getQueue();
    for (var i = 0, len = queue.length; i < len; i++) {
        utils.procedure("rejected", queue[i], err);
    }
    this.promise.setStatus("rejected", err);
}

有很多实现了promises的库供开发者可用。 像jQuery的 Deferred, 微软的 WinJS.Promise, when.js, q, 和dojo.Deferred.
关于jQuery的Deferred可以参考jQuery的deferred对象详解

Promise MDN

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79070.html

相关文章

  • ES2017异步函数现已正式可用

    摘要:标准已于年月份正式定稿了,并广泛支持最新的特性异步函数。为了领会,我们需要回到普通回调函数中进一步学习。从此编写回调函数不再那么痛苦。回调是一个函数,可以将结果传递给函数并在该函数内进行调用,以便作为事件的响应。 ES2017标准已于2017年6月份正式定稿了,并广泛支持最新的特性:异步函数。如果你曾经被异步 JavaScript 的逻辑困扰,这么新函数正是为你设计的。 异步函数或多或...

    android_c 评论0 收藏0
  • 自动化测试(未完,后续学习之后会补充更具体的)

    摘要:断言断言是什么模块提供了一组简单的断言测试,可用于测试不变量。环境是他们不必设置大量配置的环境,而是开发人员可以编写代码并从测试中获得即时反馈的地方。每当测试时,结果将出现在您的拉取请求中,您的历史记录将在其控制面板中提供。 Node assert (断言) 断言是什么 assert 模块提供了一组简单的断言测试,可用于测试不变量。 存在严格模式(strict)和遗留模式(legacy...

    姘存按 评论0 收藏0
  • 【全文】狼叔:如何正确的学习Node.js

    摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...

    Edison 评论0 收藏0
  • 【全文】狼叔:如何正确的学习Node.js

    摘要:感谢大神的免费的计算机编程类中文书籍收录并推荐地址,以后在仓库里更新地址,声音版全文狼叔如何正确的学习简介现在,越来越多的科技公司和开发者开始使用开发各种应用。 说明 2017-12-14 我发了一篇文章《没用过Node.js,就别瞎逼逼》是因为有人在知乎上黑Node.js。那篇文章的反响还是相当不错的,甚至连著名的hax贺老都很认同,下班时读那篇文章,竟然坐车的还坐过站了。大家可以很...

    fengxiuping 评论0 收藏0

发表评论

0条评论

aaron

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<