Promise 对象

Posted sakurayeah

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Promise 对象相关的知识,希望对你有一定的参考价值。

es6 Promise 对象

定义参考

案例源码 戳这里

ps:每个案例都是基于上一个改造的




一、Promise 简介

Promise 是一个对象,从它可以获取异步操作的消息

案例1

新建项目

[demo]
  |-- src
    |-- index.html
    |-- index.js
  |-- webpack.config.js
  |-- package.json

demo/package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "dev": "webpack-dev-server",
    "build": "webpack -p"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "less-loader": "^4.0.5",
    "less": "^2.7.3",
    "style-loader": "^0.19.0",
    "css-loader": "^0.28.7",
    "extract-text-webpack-plugin": "^3.0.2",
    "html-webpack-plugin": "^2.30.1",
    "webpack": "^3.10.0",
    "webpack-dev-server": "^2.9.5"
  },
  "dependencies": {
    "jquery": "^3.3.1"
  }
}

demo/webpack.config.js

var webpack = require(‘webpack‘);
var HtmlWebpackPlugin = require(‘html-webpack-plugin‘);
var ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  entry: { // 入口文件地址
    index: ‘./src/index.js‘
  },
  output: { // 出口
    path: __dirname + "/build", // 打包后的文件存放路径
    filename: ‘[name].js‘ // 文件名,name即为entry的key
  },
  module: {
    loaders: [
      {
        test: /\\.(js)$/,  // js-loader
        loader: ‘babel-loader?presets[]=es2015‘
      },
      {
        test: /\\.css$/, // css-loader
        loader: ExtractTextPlugin.extract(‘css-loader‘)
      },
      {
        test: /\\.less/, // less-loader
        loaders: ExtractTextPlugin.extract(‘css-loader!less-loader‘)
      }
    ],
  },
  devServer: {
    contentBase: ‘./build‘,
    inline: true,
    hot: true,
    before: (app) =>{
      app.get(‘/one.json‘, function(req, res) {
        res.json({ 
          user: ‘promise‘,
          success: true
        });
      });
      app.get(‘/two.json‘, function(req, res) {
        res.json({ 
          age: ‘11‘,
          success: true
        });
      });
      app.get(‘/three.json‘, function(req, res) {
        res.json({ 
          hobby: ‘basketball‘,
          success: true
        });
      });
    }
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(), // 启用热替换模块
    new HtmlWebpackPlugin({
      filename: ‘index.html‘, // 生成的的html文件名
      template: ‘./src/index.html‘, // 被打包的html路径
      chunks: [‘index‘] // 需要引入的js,对应entry的key
    }),
    new ExtractTextPlugin({ // 单独打包css
      filename: ‘[name].css‘
    })
  ]
}

webpack 相关讲解戳这里

demo/src/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Promise</title>
</head>
<body>
</body>
</html>

demo/src/index.js

// console.dir() 可以显示一个对象所有的属性和方法。
console.dir(Promise)

运行 npm i , 运行 npm run dev , 打开 http://localhost:8080 效果如下

技术分享图片

从上面的图中,可以看出 Promise 是一个构造函数,有 all、race、reject、resolve 这几个方法,原型上有then、catch等方法

Promise 的基本用法

// Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject
// resolve 异步操作执行成功后的回调函数 
// reject 异步操作执行失败后的回调函数
const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

// 用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数
// then 方法可以接受两个回调函数作为参数, 第二个函数是可选的,不一定要提供
promise.then(function(value) {
  // resolve 异步成功的操作
}, function(error) {
  // reject 异步失败的操作
});




二、resolve、reject

案例2

demo/src/index.js

import $ from ‘jquery‘;

const promise = new Promise(function(resolve, reject) {
  console.log(‘Promise‘);

  // 这里当 ajax 失败 ,或 success 不为 true 时,都认为是请求失败
  $.ajax({
    url: ‘one.json‘,
    success: (data = {}) => {
      if (data.success) {
        resolve(data);
      } else {
        reject(data);
      }
    },
    error: (err = {}) => {
      reject(err);
    }
  })
});

promise.then(function(value) {
  console.log(‘resolved‘, value)
}, function(error) {
  console.log(‘rejected‘, ‘请求失败‘)
});

效果如下

技术分享图片

可以自己尝试下, 将 demo/webpack.config.js 里,one.json 请求的 success 改为 false , 如下

{
  devServer: {
    before: (app) =>{
      app.get(‘/one.json‘, function(req, res) {
        res.json({ 
          user: ‘promise‘,
          success: false
        });
      });
    }
  }
}

效果如下 (重启服务)

技术分享图片

案例2 中,Promise 新建后立即执行,所以首先 console 打印的是 Promise。然后,then 方法里的函数,就相当于是我们平时的回调函数,只有在 promise 这个异步任务完成后才能执行,所以 console 打印的 resolved 和 rejected 在后面输出。

案例3: 链式操作

先对 案例2 进行一个简单的封装

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

// 传入 url
getJSON({url: ‘one.json‘}).then(function(value) {
  console.log(‘resolved one‘, value)
}, function(error) {
  console.log(‘rejected one‘, ‘请求失败‘)
});

效果和 案例2 一样

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

// 这里为了代码看的清除,只写了 then 方法里的第一个函数
// 这样能够按顺序,输出每个异步回调中的内容
getJSON({url: ‘one.json‘})
.then((value) => {
  // one.json 请求成功拿到的结果
  console.log(‘resolved one‘, value);

  // 进行 two.json 请求
  // return 的是 Promise 对象
  // 这里的数据能在下一个 then 方法中拿到
  return getJSON({url: ‘two.json‘});
})
.then((value) => {
  // two.json 请求成功拿到的结果
  console.log(‘resolved two‘, value);
}

效果如下
技术分享图片

第一个 then 方法中, return 的是 Promise 对象,也可以直接 return 数据,在下一个 then 方法中可以拿到数据,如下

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

getJSON({url: ‘one.json‘})
.then((value) => {
  // one.json 请求成功拿到的结果
  console.log(‘resolved one‘, value);

  // return 的是 Promise 对象, two.json 请求的数据能在下一个 then 方法中拿到
  return getJSON({url: ‘two.json‘});
})
.then((value) => {
  // 直接返回数据
  console.log(‘two‘, value);
  return value;
})
.then((value) => {
  // two.json 请求成功拿到的结果
  console.log(‘resolved two‘, value);
},(value) => {
  // two.json 请求失败拿到的结果
  console.log(‘rejected two‘, value);
})

效果如下

技术分享图片

可以自己尝试下,将 demo/webpack.config.js 里,two.json 请求的 success 改为 false , 如下

{
  devServer: {
    before: (app) =>{
      app.get(‘/two.json‘, function(req, res) {
        res.json({ 
          age: ‘11‘,
          success: false
        });
      });
    }
  }
}

效果如下

技术分享图片

第二个 then 方法,不管 two.json 请求是成功还是失败,都会返回 value

第三个 then 方法里,第一个函数是 two.json 请求成功时调用,第一个函数是 two.json 请求失败时调用




三、catch

和 then 的第二个参数一样,用来指定 reject 的回调

案例4

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

getJSON({url: ‘one.json‘})
.then((value) => {
  // one.json 请求成功拿到的结果
  console.log(‘resolved‘, value);
})
.catch((reason) => {
  // one.json 请求失败拿到的结果
  console.log(‘rejected‘, reason);
});

效果如下

技术分享图片

可以自己尝试下, 将 demo/webpack.config.js 里,one.json 请求的 success 改为 false ,就可以看到 rejected 的效果, 如下

技术分享图片

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

getJSON({url: ‘one.json‘})
.then((value) => {
  // one.json 请求成功拿到的结果
  console.log(‘resolved‘, value);
  // 这里 aaa 未定义
  console.log(aaa);
})
.catch((reason) => {
  console.log(‘rejected‘);
  // one.json 请求失败拿到的结果
  console.log(‘reason‘, reason);
});

效果如下

技术分享图片

aaa 未定义,如果不写 catch 部分代码运行到这里就会报错, 不往下运行了,如下

技术分享图片

写了 catch 后,这里就进入了 catch ,并且将报错原因也传到 reason 中去了

reason ReferenceError: aaa is not defined

这么写,即便是有错误的代码也不会报错,和 try/catch 相同。

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




四、finally

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

案例5

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

getJSON({url: ‘one.json‘})
.then((value) => {
  // one.json 请求成功拿到的结果
  console.log(‘resolved‘, value);
})
.catch((reason) => {
  // one.json 请求失败拿到的结果
  console.log(‘rejected‘, reason);
})
.finally(() => {
  console.log(‘finally‘)
});

效果如下

技术分享图片

reject 的情况大家自己尝试




五、Promise.all()

提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调

案例6

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

// all接收一个数组参数
Promise
.all([getJSON({url: ‘one.json‘}), getJSON({url: ‘two.json‘})])
.then((data) => {
  console.log(‘resolved‘, data);
})
.catch((err) => {
  console.log(‘rejected‘, err);
})
.finally(() => {
  console.log(‘finally‘)
});

all 接受一个数组作为参数,里面的值最终都算返回Promise对象

效果如下

技术分享图片

当这两个请求都请求成功后,就进入 then 里,返回的结果是把两个请求的结果塞入一个数组中去了。

Promise.all() 可以并行执行多个异步操作,并且在一个回调函数中能拿到所有的返回结果。

当 one.json 和 two.json 的 success 都为 false (webpack.config.js里修改),结果如下

技术分享图片

当 one.json 的 success 为 false, two.json 的 success 为 true ,结果如下

技术分享图片

当 one.json 的 success 为 true, two.json 的 success 为 false ,结果如下

技术分享图片

从这些可以得出,all里,只要有一个被 rejected,就会走到 catch 里去,catch 里 console 的值,是第一个被 rejected 的返回值




六、 Promise.race()

写法和 Promise.all() 类似,区别在于,race 里的异步操作,谁先完成,then 里的返回值就是谁的。

案例7

demo/src/index.js

import $ from ‘jquery‘;

const getJSON = (opts) => {
  // ajax 需要的参数都可以写在 opts 里
  console.log(‘opts‘, opts)
  const {
    url = ‘‘,
    type = ‘GET‘,
  } = opts;
  const promise = new Promise(function(resolve, reject) {
    $.ajax({
      url,
      type,
      success: (data = {}) => {
        if (data.success) {
          resolve(data);
        } else {
          reject(data);
        }
      },
      error: (err = {}) => {
        reject(err);
      }
    })
  });
  // 返回一个Promise对象
  return promise;
}

// all接收一个数组参数
Promise
.race([getJSON({url: ‘one.json‘}), getJSON({url: ‘two.json‘})])
.then((data) => {
  console.log(‘resolved‘, data);
})
.catch((err) => {
  console.log(‘rejected‘, err);
})
.finally(() => {
  console.log(‘finally‘)
});

结果如下

技术分享图片

one.json 先求情完毕,then 里的 console 打印出来的就是 one.json 返回的结果。

如果给 $.ajax 外层包一个 setTimeout ,且判断下如果是 one.json 的时候就延迟一秒,如下

setTimeout(()=>{
  $.ajax({
    url,
    type,
    success: (data = {}) => {
      if (data.success) {
        resolve(data);
      } else {
        reject(data);
      }
    },
    error: (err = {}) => {
      reject(err);
    }
  })
}, url === ‘one.json‘ ? 1000 : 0)

效果如下
技术分享图片

这里 two.json 先请求完毕,因此 then 里的 console 打印出来的就是 two.json 返回的结果。

(ps: 下面是不加 setTimeout 的三种情况)

当 one.json 和 two.json 的 success 都为 false (webpack.config.js里修改),结果如下

技术分享图片

当 one.json 的 success 为 false, two.json 的 success 为 true ,结果如下

技术分享图片

当 one.json 的 success 为 true, two.json 的 success 为 false ,结果如下

技术分享图片

从这些可以得出,race 里:

  1. 最快完成的那个,如果走到 rejected,不管后面是 rejected 还是 resolved, 都会进入 catch , 且 catch 里 console 的值,就是最快被 rejected 的值

  2. 最快完成的那个,如果走到 resolved,不管后面是 rejected 还是 resolved,
    都会进入 then , 且 then 里 console 的值,就是最快被 resolved 的值

上面的几种情况,如果你注意看 Network 的话,就会发现,不管 one.json 有没有执行成功,two.json 的请求都还是会发送

技术分享图片

我们也可以通过在 ajax 里打印 console 来看,如下

$.ajax({
  url,
  type,
  success: (data = {}) => {
    if (data.success) {
      resolve(data);
      console.log(‘请求成功‘, url)
    } else {
      reject(data);
      console.log(‘请求失败‘, url)
    }
  },
  error: (err = {}) => {
    reject(err);
    console.log(‘请求失败‘, url)
  }
})

效果如下

技术分享图片

当 then 里的回调开始执行时,getJSON({url: ‘two.json‘}) 并没有停止,仍旧再执行。




七、Promise.resolve()

有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用

具体参考戳这里

案例8

demo/src/index.js

const obj = {
  a: 1,
  b: 2
}

const promise = Promise.resolve(obj);

console.log(promise)

promise.then((v) => {
  console.log(v)
})

结果如下

技术分享图片




八、Promise.resolve()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

具体参考戳这里

案例9

demo/src/index.js

const obj = {
  a: 1,
  b: 2
}

const promise = Promise.reject(obj);

console.log(promise)

promise.then(v => {
  // do nothing
}, v => {
  console.log(v)
})

promise.catch(v => {
  console.log(v)
})

结果如下

技术分享图片




















以上是关于Promise 对象的主要内容,如果未能解决你的问题,请参考以下文章

前端面试题之手写promise

前端片段整理

什么时候然后从Promise.all()的子句运行?

ES6 promise对象

JavaScript - Promise对象

Promise对象