Skip to content

Latest commit

 

History

History
309 lines (229 loc) · 6.97 KB

10.Promise对象.md

File metadata and controls

309 lines (229 loc) · 6.97 KB

目录

  • Promise 的含义
  • 基本用法
  • 错误进阶
  • 应用
  • Promise 的实现原理

1. Promise 的含义

Promise 是异步编程的一种解决方案,避免了传统的回调函数的层层嵌套,也就是常说的“回调地狱”。

Promise 一旦新建就会立即执行,无法中途取消。

2. 基本用法

ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例。

const promise = new Promise (function (resolve, reject) {
  if (/* 异步操作成功*/) {
    resolve(value);
  } else {
    /* 操作失败 */
    reject(error);
  }
});

Promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。

promise.then(function(value) {
  // success
}, function (error) {
  // failure
});

Promise 新建后就会立即执行

let promise = new Promise(function (resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function(){
  console.log('resolved');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then 方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以 resolved 最后输出。

一般来说,调用 resolvereject 以后,Promise 的使命就完成了,后继操作应该放到 then 方法里面,而不应该直接写在 resolvereject 的后面。所以,最好在它们前面加上 return 语句,这样就不会有意外。

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2); //虽然此句会执行,但是不建议这样做
}).then(r => {
  console.log(r);
});
// 2
// 1

// �resolve 前加 return
new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的语句不会执行
  console.log(2);
})

3. 错误进阶

3.1 你不知道的 Promise.resolve

Promise.resolve() 是一种简化写法。例如如下代码

new Promise(function (resolve, reject) {
  resolve(someSynclhronousValue);
}).then(/*...*/);

使用 Promise.resolve 会更加简洁

Promise.resolve(someSynclhronousValue).then(/*...*/);

3.2 catch() 与 then(null, ...) 并非完全等价

catch() 仅仅是一个语法糖,下面的代码是等价的:

somePromise().catch(function(err){
  // handle error
});

// 等价于

somePromise().then(null, function(err){
  // handle error
});

然而,这并不意味着下面两段代码是等价的:

somePromise().then(function(){
  return someOtherPromise();
}).catch(function(err){
  // handle err
});

// 不等价

somePromise().then(function(){
  return someOtherPromise();
}, function(err){
  // handle error
});

上面的代码不等价的原因:当使用 then(resolveHandler, rejectHandler) 这种形式时,rejectHandler 并不会捕获由 resolveHandler 引发的异常。例如如下的代码,就很明显看出二者的区别。

somePromise().then(function(){
  throw new Error('oh noes');
}).catch(function(err){
  // I caught your error!
});

// 下面的代码则捕获不了� error

somePromise().then(function(){
  throw new Error('oh noes');
}, function(err){
  // I didn't caught your error!
});

一般来说,不要在 then 方法里面定义 Reject 状态的回调函数(即 then 的第二个参数),总是使用 catch 方法。

// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });

3.3 promise 穿透

查看一下代码会打印出什么?

Promise.resolve('foo').then(Promise.resolve('bar')).then(function(result){
  console.log(result);
});

如果你认为它会打印出 bar,那么你就错了。它实际上打印出来的是 foo

发生这个的原因是如果你向 then() 传递的并非是一个函数(比如 promise ),它实际上会将其解释为 then(null),这就会导致前一个 promise 的结果会穿透下面。你可以自己测试一下:

Promise.resolve('foo').then(null).then(function (result) {
  console.log(result); // foo
});

添加任意数量的 then(null),它依然会打印 foo

简单的说,你可以直接传递一个 promisethen() 函数中,但是它并不会按照你期望的去执行。then() 是期望获取一个函数,因此你希望做的最可能是:

Promise.resolve('foo').then(function () {
  return Promise.resolve('bar');
}).then(function (result) {
  console.log(result); // bar
});

这样他就会如我们所想的打印出 bar。

因此记住:永远都是往 then() 中传递函数!

比较以下 4 个 Promise 代码的不同之处。

问题1

doSomeThing().then(function () {
  return doSomeThingElse();
}).then(finalHandler);

// 第一个 then 中传入的是函数,并且有返回值

// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(resultOfDoSomeThingElse)

问题2

doSomeThing().then(function () {
  doSomeThingElse();
}).then(finalHandler);

// 第一个 then 中传入的是一个函数,但是 并没有返回值

// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(undefined)

问题3

doSomeThing().then(doSomeThingElse()).then(finalHandler);

// 第一个 then 中传入的是一个函数执行的结果,而并非是一个函数,所以 最后的 then 中的参数是 doSomeThing 的执行结果

// doSomeThing --> doSomeThingElse(undefined) --> finalHandler(resultOfDoSomeThing)

问题4

doSomeThing().then(doSomeThingElse).then(finalHandler);

// 第一个 then 中传入的是一个函数,所以 最后的 then 中的参数是 doSomeThing 的执行结果

// doSomeThing --> doSomeThingElse(resultOfDoSomeThing) --> finalHandler(resultOfDoSomeThingElse)

4. 应用

实例:异步加载图片

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

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

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

    image.src = url;
  });
}

实例:封装 Ajax

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

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

  return promise;
};

getJSON('/nameList.json').then(function (json) {
  console.log('contents' + json);
}, function (error) {
  console.error('出错了', error);
});