Promise 总结

欢迎访问我的个人博客 晓东的博客

Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效,使得 Node.js 应用的越来越广泛;Node.js 是单线程的,所以需要按顺序执行异步逻辑时一般采用后续传递风格,也就是将后续逻辑封装在回调函数中作为起始函数的参数,逐层嵌套,这会导致所谓的 “回调地域”。这种情况下,Promise 等其他异步编程解决方案应运而生。

Promise是什么?

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,是抽象异步处理对象以及对其进行各种操作的组件。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
ES6 中新增的规范对象。

Promise 流程

示例代码

function asyncFunction() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('Async Hello world');
        }, 16);
    });
}

asyncFunction().then(function (value) {
    console.log(value); // => 'Async Hello world'
}).catch(function (error) {
    console.log(error);
});
  1. new Promise构造器之后,会返回一个promise对象
  2. 为 promise 对象用设置 .then 调用返回值时的回调函数。asyncFunction 这个函数会返回 promise 对象, 对于这个 promise 对象,我们调用它的 then 方法来设置 resolve 后的回调函数, catch 方法来设置发生错误时的回调函数。
  3. 该promise对象会在setTimeout之后的16ms时被resolve, 这时 then 的回调函数会被调用,并输出 ‘Async Hello world’ 。
  4. 在这种情况下 catch 的回调函数并不会被执行(因为promise返回了resolve), 不过如果运行环境没有提供 setTimeout 函数的话,那么上面代码在执行中就会产生异常,在 catch 中设置的回调函数就会被执行。
  5. 当然,像promise.then(onFulfilled, onRejected) 的方法声明一样, 如果不使用catch 方法只使用 then 方法的话,如下所示的代码也能完成相同的工作。代码如下:
asyncFunction().then(function (value) {
    console.log(value);
}, function (error) {
    console.log(error);
});

Promise 状态

promise对象有以下三个状态:
1. Fulfilled:resolve(成功)时。此时会调用 onFulfilled
2. Rejected:reject(失败)时。此时会调用 onRejected
3. Pending:既不是resolve也不是reject的状态。也就是promise对象刚被创建后的初始化状态等

说明

  • promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再发生任何变化。也就是说,Promise与Event等不同,在.then 后执行的函数可以肯定地说只会被调用一次。
  • 另外,Fulfilled和Rejected这两个中的任一状态都可以表示为Settled(不变的)。
  • Settled:resolve(成功) 或 reject(失败)。
  • 当promise的对象状态发生变化时,用.then 来定义只会被调用一次的函数。

Promise API

promise.then(onFulfilled, onRejected)

var promise = new Promise(function(resolve, reject){
    resolve("传递给then的值");
});
promise.then(function (value) {
    console.log(value);
}, function (error) {
    console.error(error);
});

这段代码创建一个promise对象,定义了处理onFulfilled和onRejected的函数
(handler),然后返回这个promise对象。
这个promise对象会在变为resolve或者reject的时候分别调用相应注册的回调函数。
• 当handler返回一个正常值的时候,这个值会传递给promise对象的onFulfilled方法。
• 定义的handler中产生异常的时候,这个值则会传递给promise对象的onRejected方
法。

promise.catch(onRejected)

var promise = new Promise(function(resolve, reject){
    resolve("传递给then的值");
});
promise.then(function (value) {
    console.log(value);
}).catch(function (error) {
    console.error(error);
});

这是一个等价于promise.then(undefined, onRejected) 的语法糖。

Promise.resolve

Promise.resolve(promise);
Promise.resolve(thenable);
Promise.resolve(object);
var taskName = "task 1"
asyncTask(taskName).then(function (value) {
    console.log(value);
}).catch(function (error) {
    console.error(error);
});
function asyncTask(name){
    return Promise.resolve(name).then(function(value){
        return "Done! "+ value;
    });
}

根据接收到的参数不同,返回不同的promise对象。
虽然每种情况都会返回promise对象,但是大体来说主要分为下面3类。
1. 接收到promise对象参数的时候,返回的还是接收到的promise对象
2. 接收到thenable类型的对象的时候,返回一个新的promise对象,这个对象具有一个 then 方法
3. 接收的参数为其他类型的时候(包括 JavaScript 对象或 null 等),返回一个将该对象作为值的新 promise 对象

Promise.reject(object)

返回一个使用接收到的值进行了reject的新的promise对象。
而传给Promise.reject的值也应该是一个 Error 类型的对象。
另外,和 Promise.resolve不同的是,即使Promise.reject接收到的参数是一个promise对
象,该函数也还是会返回一个全新的promise对象。

Promise.all(promiseArray)

var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.all([p1, p2, p3]).then(function (results) {
console.log(results); // [1, 2, 3]
});

生成并返回一个新的promise对象。
参数传递promise数组中所有的promise对象都变为resolve的时候,该方法才会返回, 新
创建的promise则会使用这些promise的值。
如果参数中的任何一个promise为reject的话,则整个Promise.all调用会立即终止,并返
回一个reject的新的promise对象。
由于参数数组中的每个元素都是由 Promise.resolve 包装(wrap)的,所以Paomise.all
可以处理不同类型的promise对象。

Promise.race(promiseArray)

var p1 = Promise.resolve(1),
p2 = Promise.resolve(2),
p3 = Promise.resolve(3);
Promise.race([p1, p2, p3]).then(function (value) {
console.log(value); // 1
});

生成并返回一个新的promise对象。
**参数 promise 数组中的任何一个 promise 对象如果变为 resolve 或者 reject 的话, 该函数就
会返回**,并使用这个 promise 对象的值进行 resolve 或者 reject。

Promise.all() 和 Promise.race() 的区别

Promise.all()
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();
// 所有promise变为resolve后程序退出
Promise.all([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (values) {
    console.log(Date.now() - startDate + 'ms');
    // 約128ms
    console.log(values); // [1,32,64,128]
});
Promise.race()
function timerPromisefy(delay) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
// 任何一个promise变为resolve或reject 的话程序就停止运行
Promise.race([
    timerPromisefy(1),
    timerPromisefy(32),
    timerPromisefy(64),
    timerPromisefy(128)
]).then(function (value) {
    console.log(value); // => 1
});

附加方法

done()

Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

asyncFunc()
  .then(f1)
  .catch(r1)
  .then(f2)
  .done();
实现方法
Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => { throw reason }, 0);
    });
};

从上面代码可见,done方法的使用,可以像then方法那样用,提供Fulfilled和Rejected状态的回调函数,也可以不提供任何参数。但不管怎样,done都会捕捉到任何可能出现的错误,并向全局抛出。

finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

下面是一个例子,服务器使用Promise处理请求,然后使用finally方法关掉服务器。

server.listen(0)
  .then(function () {
    // run test
  })
  .finally(server.stop);
实现方法
Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};

上面代码中,不管前面的Promise是fulfilled还是rejected,都会执行回调函数callback。

使用案例

异步加载图片

function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    var image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}

用Promise对象实现的Ajax操作

var getJSON = function(url) {
  var promise = new Promise(function(resolve, reject){
    var client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

    function handler() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出错了', error);
});

jquery 中的 Ajax

// 常规写法
 $.ajax({
     url: '/api/test',
     type: 'POST',
     data: {...},
     success: function(res) {
         // dosomething
     },
     fail: function(res) {
         // dosomething
     },
     complete: function() {
         // dosomething
     }
 })

// 新的写法
$.ajax({
     url: '/api/test',
     type: 'POST',
     ...
 })
 .done(function(res) {
     // success and do something
 })
 .fail(function(res) {
     // fail and do something
 })
 .always(function() {})

参考资料

  1. ECMAScript 6 入门
  2. Promise 迷你书
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值