ES6 - Promise普罗米修斯

在项目安装盘中,需要写入数据库后,再进行邮件发送,由此为了让异步流程更直观,提供一下promise使用的基本说明

相关核心阅读

优点

链式写法:语义化同步写法实现异步callback结果

基本概念

  • promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
  • promise对象处理异步操作,通过两个函数方法传递结果(一般使用resolve指代成功,reject指代失败)

    • 成功:resolve('操作成功返回的值')
    • 失败: reject('操作失败返回的值')
  • 异步返回结果,可通过then链式写法获取上一步返回的值,失败返回的错误可通过catch获取

var po = new Promise((resolve,reject)=>{...})
po
  .then(step2)
  .then(step3)
  .then(step4)
  .catch((err)=>{console.log(err)})
  • promise所有的接口如promise.all,promise.race,promise().then,均返回一个promise对象

基本用法

  • 无需传参,可直接使用new Promise:

    let po = new Promise((resolve, reject) => {
    setTimeout(()=>{
      console.log('wait 1 seconds...');
      let score = parseInt(Math.random() * 100);
      score > 60 ? resolve(`及格: ${score}`): reject(`不及格: ${score}`)
    },1000);
    })
    
    // 执行
    po
    .then((val) => {console.log(val)})
    .catch((err) => {console.log(err)})
    
    // 简单传递,也可使用如下写法合并then & catch
    po.then((val) => {console.log(val)}, (err) => {console.log(err)})

    输出结果:

    wait 1 seconds...
    不及格: 14

    在线查看demo

  • 有参数,使用function包裹,return需要的promise对象:

    let doSome = function(time) {
    return new Promise((resolve,reject) => {
      setTimeout(()=>{
        console.log(time,' seconds exec!');
        resolve(time + 1000);
      },time)
    })
    }
    
    let doSomeThing = function(time) {
    console.log(time, ' seconds doSomethins show!')
    return time + 1000
    }
    
    let final = function(time) {
    console.log(time, ' seconds final get!')
    }
    
    doSome(1000)
    .then((val)=>{return doSomeThing(val)})
    .then((val)=>{final(val)})
    .catch((err)=>{console.log(err)})

    输出结果

    1000 " seconds exec!"
    2000 " seconds doSomethins show!"
    3000 " seconds final get!

    在线查看demo

    示例需要注意:

    • 如后续还有then,需要当前阶段使用return返回以供后续调取参数
    • 开始执行时()doSome)需要定义promise对象,后续每一步均可使用普通函数正常返回(如本例doSomeThing未定义为promise对象)

  • 多异步操作:

    let doSome = function(time) {
    return new Promise((resolve,reject) => {
      setTimeout(()=>{
        console.log(time,' seconds exec!');
        resolve(time + 1000);
      },time)
    })
    }
    
    let doSomeThing = function(time) {
    return new Promise((resolve,reject)=>{
      setTimeout(()=>{
        console.log(time,' seconds doSomethins show!');
        resolve(time + 1000);
      },time)
    })
    }
    
    let final = function(time) {
    console.log(time, ' seconds final get!')
    }
    
    doSome(1000)
    .then((val)=>{return doSomeThing(val)})
    .then((val)=>{final(val)})
    .catch((err)=>{console.log(err)})

    输出结果:

    1000 ' seconds exec!'
    2000 ' seconds doSomethins show!'
    3000 ' seconds final get!'

    在线demo

Promise.all

Promise.all方法用于将多个 可迭代iterable参数(不一定是promise),包装成一个新的 Promise 实例.当所有实例完成后,执行then下一步操作:

let p1 = Promise.resolve(3);
let p2 = 1337;
let p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, "foo");
}); 

Promise.all([p1, p2, p3]).then(values => { 
    console.log(values); 
    // [3, 1337, "foo"] 
});

Promise.race

Promise.race方法同样是将多个 可迭代参数(不一定是promise)包装成一个新的 Promise 实例。最先完成后直接执行下一步then:

var p1 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 500, "one"); 
});
var p2 = new Promise(function(resolve, reject) { 
    setTimeout(resolve, 100, "two"); 
});

Promise.race([p1, p2]).then(function(value) {
  console.log(value); // "two"
  // 两个都完成,但 p2 更快
});

Promise.resolve

将普通对象转为promise对象,可以使用此接口快速实现,从而调用then,如下例:

const jsPromise = Promise.resolve($.ajax('/whatever.json'));
jsPromise.then().catch()

resolve后的参数:

  1. 参数是一个 Promise 实例

Promise.resolve将不做任何修改、原封不动地返回这个实例。

  1. 参数是一个thenable对象

thenable对象指的是具有then方法的对象,比如下面这个对象。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});
  1. 参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello
  1. 不带有任何参数

Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用Promise.resolve方法。

const p = Promise.resolve();

p.then(function () {
  // ...
});

此处不带参数需要注意事件顺序(event loop):立即resolve的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。

setTimeout(function () {
  console.log('three');
}, 0);

Promise.resolve().then(function () {
  console.log('two');
});

console.log('one');

// one
// two
// three

封装promise

综上所述,封装一个promise其实很简单,示例中settimeout就是封装的demo。

方法1:Promise.resolve

方法2:new Promise(resolve,reject),函数体根据回调设置.

以下为一个node-xml2js异步方法封装成Promise:

var util = require('util');
var xml2js = require('xml2js');

async function xml2JsonSample(res){
    var promise = await new Promise(function (resolve, reject) {

            xml2js.parseString(res, function (jsError, jsResult) {

                if (jsError) {
                    reject(jsError);
                } else {
                      resolve(jsResult);                     
                }  
            });  
        });  
    return promise;
}

比较new promise和 promise.resolve

在写chrome插件时遇到一个异步需要写成同步返回结果,为了省事用了promise.resolve:

        const resp = await Promise.resolve(
            $.post(
                NetConfig.listUrl,
                {
                    s: val, offset: NetConfig.offset, limit: NetConfig.limit, type: NetConfig.type
                },
                (res) => {
                    let resObj = [];
                    let resParse = JSON.parse(res).result;
                    resParse.songs.map((item, index) => {
                        resObj[index] = {
                            name: item.name,
                            id: item.id,
                            artists: item.artists.map((arts)=>{
                                return arts.name
                            }),
                            album: item.album.name
                        }
                    })
                    console.log(resObj);
                    return resObj;
                }
            )
        )

测试最终返回的resp,是异步请求返回的res,并非处理过的resObj,此种需要对返回做处理的,还是老老实实用new Promise确保没有问题

@2017-08-09 22:49