说说Promise

标题不知道怎么拟定比较好,总之是讲Promise的吧,基本上算是第一次讲Promise,以前Rails+Angular的时候曾经说过,不过那个时候对于callback hell和Promise解决的东西理解的并不深刻,所以解释的也很肤浅(不明明这次依旧是肤浅的解释)。

首先……V8的Promise性能实在不靠谱,都没有第三方的快,bluebird有一篇性能比较(反正大致是想说自己快吧):http://bluebirdjs.com/docs/benchmarks.html

慢归慢,基本上一些对于性能需求不太迫切的项目还是可以用自带的Promise的,Node6对于ES6的支持已经相当全面了,这一点可以用npm install es-checker检查一下。(毕竟回调地狱恶心到吐血)。

Promise其实并不是Javascript提出的一个概念,可以看这篇文章了解一下Promise A+的标准:http://cuipengfei.me/blog/2016/05/15/promise/

普通的对象经过封装之后就能变成Promise实例进行使用:

var promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

之后再异步操作执行完之后,根据resolve或者reject进行处理,catch操作可以代替第二个function处理error的情况。

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

Promise如果直接跟一堆then,就会直接执行下去,如果中间有异步函数,那么效果同样可能会是还没来得及执行完就到了下一个then的内容。

我们可以看一下以下例子:

const config = require('./config.json');
const fs = require('fs');
console.log(config.path.temp);

// 判断文件是否存在,存在则删除
//
const existDir = function() {
  return new Promise((resolve, reject) => {
    fs.exists(config.path.temp, (exists) => {
      resolve(exists);
    });
  })
};

existDir()
.then((value) => {
  console.log(value);
})
.then((value) => {
  if (!value) {
    fs.mkdir('111', (err)=> {
      if (!err) {
        console.log('success');
        return '1';
      } else {
        throw Error('something wrong');
      }
    })
  }
})
.then((value) => {
  console.log(value);
});

在此例中返回值为:

false
undefined
success

可以看出,先输出了第三个console.log,最后输出mkdir后的结果。

但是如果then链返回的是Promise对象,就会按照顺序依次执行。前一个的resolve返回值是后一个函数中的value。

当我们要一次执行多个Promise(非链式调用),可以使用Promise.all,它的特点是如果一个失败了,那么就执行Promise的回调函数,这样就不适用于全部都执行完再做判断的场合,如果需要,我想到的一个方法是全部都使用resolve,根据resolve传出的参数决定判断与否,但是这样其实丧失了一定的语义特征。

接下来的第二种情况也就是Promise的链式调用,也是我们过去回调地狱的情况——如果A执行完要执行B,B执行完要执行C,怎么办。

一种常见的方法是Generator+Promise,优点是语义清晰,完全跟同步逻辑一致,一目了然。

function getFoo () {
  return new Promise(function (resolve, reject){
    resolve('foo');
  });
}

var g = function* () {
  try {
    var foo = yield getFoo();
    console.log(foo);
  } catch (e) {
    console.log(e);
  }
};

function run (generator) {
  var it = generator();

  function go(result) {
    if (result.done) return result.value;

    return result.value.then(function (value) {
      return go(it.next(value));
    }, function (error) {
      return go(it.throw(error));
    });
  }

  go(it.next());
}

run(g);

以上实例来自阮一峰的ES6教程,run函数是封装后的,如果不理解,可以先看看:Promise, generator, async與ES6里面有逐步分析流程。

当然,这种方式适用于模块之间的解耦,似乎对于一些相同模块异步函数而言比较浪费,不够友好,有些时候我们也不知道到底要重复几次。用yield反而很奇怪(除了yield的部分,Generator函数其他部分和一般函数无疑,可以把yield看作断点)。

此时我们可以直接用循环,或者reduce进行操作,可以看:http://www.kancloud.cn/kancloud/promises-book/44249

然而ES7中的async/await可以完美的取代Generator+Promise的复杂模式(毕竟要自己写一个run函数)。

当然,我还是觉得,所有东西应该为逻辑的清晰服务,怎么清晰怎么来吧。

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: 知识, 代码段, 语法, node.js

添加新评论