实际情况来看,还是yield很爽
来源:互联网 发布:手机淘宝怎么改店名 编辑:程序博客网 时间:2024/06/05 08:36
转自我的博客:http://www.cnblogs.com/htoooth/p/5595923.html
0 引言
最近公司有一个 php 的项目,要 port 到 node.js 来。我之前没有接触过这个项目,整个项目使用的是 yaf 框架。整个项目流程是调用服务端的业务数据,然后拼装数据,返回给前端;前端没有做到前后端分离,还有很多的页面在服务器端进行渲染 。
1 难度
1.1 php
php 之前没有写过几行代码,但是还能看懂。yaf 也没有接触过,只到听说的程度。然后还要把一个商品的详情页面从 php 导成 js 的,我觉得难度很大,因为接口竟然没有文档,只能看着 php 代码来了解。看了两天,有点门路了,开始动手。
1.2 js
nodejs使用的是 express 框架,框架不是我选的,公司有架构师选择,只能听命。最主要的是 express 确实不难。
1.3 再次申明
接手这一个项目我对这个项目的业务内容知识为零,同志们想想怎么把php导到js.
2 核心:php是同步的,js是异步
这个工作中,最有意思的是,php 是同步的,js 是异步的,把一大把同步的 php 代码导成异步的 js,这不就是证明 php 是不是最好的语言的战场吗?
各位,你们觉得这个事该怎么做快?
2.1 思路
当时,我就想到找个人好好给我讲讲这个项目的结构阿,字段啊,什么的?发现这样不行,为什么呢?
- 没有文档,包括接口
- 别人都很忙,没有时间给你讲
- 给自己的时间也不多(要在一个星期内搞定)
- 给你讲了一大堆业务,你也记不住
于是,我就直接上代码了,决定先写代码,业务知识什么以后再补。于是这场战役就是不关业务,较纯的关于代码的比较了。
2.2 如何做-代码
导代码,其实难度不大,就语言而言,php 和 js 相差不大,php 中也就array
难以理解,因为能代表两种数据结构,返回值的时候就要好好看看,到底是返回 [] 还是 {},就这个犯了不少错,其它的就一般般了。
重点是 php 中对 业务API的访问是同步的,那叫一个简单,调用一个函数就可以了。而在 js 中就有一个大问题,js是异步的,你必须将使用结果写在回调中。一个回调还是没有问题,问题是一个函数中有多异步调用,这我还能忍。最后面一个问题直接击垮了我,因为异步,打断了写代码结构层次,而我又不懂业务,接不上了业务逻辑,这就要我了解业务,可惜基于上面的原因不行啊。
有人会说 promise 能解决你的问题,promise 顺序,但是怎么访问变量啊,几十个变量的修改,你是不是都要放在then中,还有是放在全局变量中,关键是then也破坏了整体代码结构,一眼看过去全是then,最后我也不得不放弃这个方案。
一直说async/await解决了回调的问题,自己也试过,挺好的。可是项目不让用啊。
直到我发现了co,随后又发现 bludbird 的 Promise.coroutine,才解决了我的一些问题,因为我就需要直译啊,就是照着php翻译到js过去。对比一下代码:
2.3 php业务代码
public static function getProductInfo($productId, $goodsId, $uid, $vipLevel = 0, $productSkn = null) { $goodsInfo = array(); $statGoodsInfo = array(); $banner = array(); $baseInfo = ItemData::baseInfo($productId, $uid, $productSkn);//异步 if(empty($baseInfo['productName']) && empty($baseInfo['erpProductId']) && empty($baseInfo['productPriceBo'])) { return array(); } $baseInfo['uid'] = $uid; $productId = $baseInfo['id']; $goodsInfo['name'] = $baseInfo['productName']; $goodsInfo['skn'] = $baseInfo['erpProductId']; $goodsInfo['productId'] = $productId; $goodsInfo['maxSortId'] = $baseInfo['maxSortId']; $goodsInfo['smallSortId'] = $baseInfo['smallSortId']; $goodsInfo['promotionId'] = $baseInfo['isPromotion']; $goodsInfo['goCartUrl'] = Helpers::url('/shopping/cart'); $brandId = 0; //设置并发请求数据 self::setMultiResourceByProductBaseInfo($baseInfo);//异步 if(isset($baseInfo['brand']['id'])) { $brandId = $baseInfo['brand']['id']; } //收藏喜欢 $favoriteData = self::getProductFavoriteData($uid, $productId, $brandId);//异步 // 商品标签 $goodsInfo['tags'] = self::getTagsDataByProductInfo($baseInfo);//异步 // 商品促销短语 if (!empty($baseInfo['salesPhrase'])) { $goodsInfo['saleTip'] = $baseInfo['salesPhrase']; } // 商品价格 if (isset($baseInfo['productPriceBo'])) { $goodsInfo['marketPrice'] = $baseInfo['productPriceBo']['formatMarketPrice']; $goodsInfo['hasOtherPrice'] = true;//非市场价格 $goodsInfo['salePrice'] = $baseInfo['productPriceBo']['formatSalesPrice']; if($goodsInfo['marketPrice'] == $goodsInfo['salePrice']) {//价格相同,只显示市场价格 unset($goodsInfo['salePrice']); $goodsInfo['hasOtherPrice'] = false; } } //VIP数据 $goodsInfo['vipPrice'] = self::getVipDataByProductBaseInfo($baseInfo, $vipLevel, $uid);//异步 //促销活动banner $goodsInfo['activity'] = self::getActivityDataByProductBaseInfo($baseInfo);//异步 if (isset($baseInfo['productPriceBo']['yohoCoinNum']) && $baseInfo['productPriceBo']['yohoCoinNum'] !== 0) { array_push($goodsInfo['activity'], array('type' => '返YOHO币', 'des' => '每件返 ' . $baseInfo['productPriceBo']['yohoCoinNum'] . '个 YOHO币') ); } // 上市期 if (isset($baseInfo['expectArrivalTime']) && !empty($baseInfo['expectArrivalTime'])) { $goodsInfo['arrivalDate'] = $baseInfo['expectArrivalTime'] . '月'; $goodsInfo['presalePrice'] = $baseInfo['productPriceBo']['formatSalesPrice']; unset($goodsInfo['salePrice']); $goodsInfo['hasOtherPrice'] = false; } //商品咨询和评论数据 $consultComment = self::getConsultCommentDataByProductInfo($baseInfo);//异步 // 品牌信息 if (!empty($baseInfo['brand'])) { $goodsInfo['brandImg'] = Helpers::getImageUrl($baseInfo['brand']['brandIco'], 47, 47); $goodsInfo['brandName'] = $baseInfo['brand']['brandName']; $goodsInfo['brandUrl'] = Helpers::url('', array(), $baseInfo['brand']['brandDomain']); $banner = self::getBrandDataByProductBaseInfo($baseInfo);//异步 if(isset($banner['isCollect']) && isset($favoriteData['brand'])) { $banner['isCollect'] = $favoriteData['brand']; } } //sku商品信息 $skuData = self::getSkuDataByProductBaseInfo($baseInfo);//异步 $goodsInfo['img'] = $skuData['defaultImage']; $goodsInfo['colors'] = $skuData['skuGoods']; $totalStorageNum = $skuData['totalStorageNum']; // 是否收藏 $goodsInfo['isCollect'] = $favoriteData['product']; // 限购商品 if ($baseInfo['isLimitBuy'] === 'Y') { // 是否开售 $isBeginSale = (isset($baseInfo['saleStatus']) && $baseInfo['saleStatus'] == 1); // 限购商品有关的展示状态 $showStatus = 1; if (isset($baseInfo['showStatus'])) { $showStatus = intval($baseInfo['showStatus']); } $fashTopGoods = self::getFashionTopGoodsStatus($uid, $showStatus, $isBeginSale);//异步 //潮流尖货状态 $goodsInfo['fashionTopGoods'] = array( 'getLimitedCode' => $fashTopGoods['getLimitedCode'],//限购码状态 'hasLimitedCode' => $fashTopGoods['hasLimitedCode'],//是否已经获取限购码 'limitedCodeSoldOut'=> $fashTopGoods['limitedCodeSoldOut'],//限购码是否已经抢光 'getLimitedCodeDis' => $fashTopGoods['getLimitedCodeDis'],//限购码是否失效 ); if($fashTopGoods['soldOut']) { $goodsInfo['soldOut'] = $fashTopGoods['soldOut']; $totalStorageNum = 0;//改总数为已售磬 } else { $goodsInfo['openSoon'] = $fashTopGoods['openSoon'];//即将开售 $goodsInfo['dis'] = $fashTopGoods['dis'];//是否失效 $goodsInfo['buyNow'] = $fashTopGoods['buyNow'];//是否立即购买 } } $soldOut = $baseInfo['status'] == 0 || $totalStorageNum === 0; $notForSale = $baseInfo['attribute'] == 2;//非卖品 $virtualGoods = $baseInfo['attribute'] == 3;//虚拟商品 if (!$soldOut && !$notForSale && !$virtualGoods) { $goodsInfo['addToCart'] = true; //立即购买或者即将开售存在 if((isset($goodsInfo['buyNow']) && $goodsInfo['buyNow']) || (isset($goodsInfo['openSoon']) && $goodsInfo['openSoon'])) { unset($goodsInfo['addToCart']); } }// 非卖品 elseif ($notForSale) { $goodsInfo['notForSale'] = true; } // 已售磬 elseif ($soldOut) { $goodsInfo['soldOut'] = true; unset($goodsInfo['fashionTopGoods']); } //虚拟商品 else if($virtualGoods) { $goodsInfo['buyNow'] = true;//是否立即购买 $goodsInfo['buyNowBase'] = Helpers::url('/ticket', array(), 'shopping'); $goodsInfo['virtualGoods'] = $virtualGoods; if(isset($goodsInfo['salePrice'])) { $goodsInfo['advancePrice'] = $goodsInfo['salePrice'];//先行价格 unset($goodsInfo['salePrice']); } } //去掉即将售罄 if(empty($totalStorageNum) || $soldOut) { if(isset($goodsInfo['tags']['isFew'])) { unset($goodsInfo['tags']['isFew']);//去掉即将售罄 } } //分享相关 $goodsInfo['weixinUrl'] = Helpers::url($_SERVER['REQUEST_URI'], array(),'item'); $goodsInfo['sharedTitle'] = $goodsInfo['name']; $goodsInfo['shareImg'] = $goodsInfo['img']; $goodsInfo['shareDesc'] = $baseInfo['phrase']; //统计需要的商品信息 $statGoodsInfo['uid'] = $uid; $statGoodsInfo['skn'] = $baseInfo['erpProductId']; $statGoodsInfo['productId'] = $productId; $statGoodsInfo['productName'] = str_replace("'", "’",$goodsInfo['name']); $statGoodsInfo['brandName'] = empty($goodsInfo['brandName'])? '' : str_replace("'", "’", $goodsInfo['brandName']); $statGoodsInfo['marketPrice'] = str_replace('¥', '', $goodsInfo['marketPrice']); if(isset($goodsInfo['salePrice'])) { $statGoodsInfo['salePrice'] = str_replace('¥', '', $goodsInfo['salePrice']); } else { $statGoodsInfo['salePrice'] = str_replace('¥', '', $goodsInfo['marketPrice']); } if (!empty($banner['brandId'])) { $domainBrand = BrandsModel::getBrandByDomain($banner['brandDomain']); if (!empty($domainBrand['type']) && !empty($domainBrand['shopId'])) { switch (intval($domainBrand['type'])) { case 1: //多品店不显示 $banner = array(); break; case 2: //单品店显示新版的店铺banner $basisData = ShopModel::basisTemplate($domainBrand['shopId']); $banner['bgImg'] = empty($basisData['shopTopBanner']['banner']) ? $banner['bgImg'] : $basisData['shopTopBanner']['banner']; break; } } } $statGoodsInfo['imageUrl'] = $goodsInfo['img']; $statGoodsInfo['productUrl'] = $goodsInfo['weixinUrl']; $statGoodsInfo['smallSortId'] = $goodsInfo['smallSortId']; $statGoodsInfo['soldOut'] = intval($soldOut); return array('goodsInfo'=> $goodsInfo,'consultComment' => $consultComment, 'banner'=> $banner,'statGoodsInfo' => $statGoodsInfo); }
标异步的地方在函数中有十几处,你怎样解决呢?看懂业务后重构?
2.4 导成 javascript 的业务代码
这是js代码,使用 yield 解决问题
const detailDataPkg = (origin, uid, vipLevel, ua) => { return co(function*() { let result = {}; // 结果输出 // 商品名称 if (!origin.productName) { return result; } origin.uid = uid; result.name = origin.productName; result.skn = origin.erpProductId; result.productId = origin.id; result.maxSortId = origin.maxSortId; result.smallSortId = origin.smallSortId; result.promotionId = origin.isPromotion; result.goCartUrl = helpers.urlFormat('/shopping/cart'); // 接口处理数据,设置并发请求数据 yield setMultiResourceByProductBaseInfo(origin); let brandId = 0; if (origin.brand.id) { brandId = origin.brand.id; } // 处理收藏喜欢数据 let favoriteData = yield getProductFavoriteData(uid, result.productId, brandId); // 商品标签 result.tags = getTagsDataByProductInfo(origin); // 商品促销短语 if (origin.salesPhase) { result.saleTip = origin.salesPhrase; } // 商品价格 if (origin.productPriceBo) { result.marketPrice = origin.productPriceBo.formatMarketPrice; result.hasOtherPrice = true; result.salePrice = origin.productPriceBo.formatSalesPrice; if (result.marketPrice === result.salePrice) { delete result.salePrice; result.hasOtherPrice = false; } } // VIP数据 result.vipPrice = getVipDataByProductBaseInfo(origin, vipLevel, uid); // 促销活动banner result.activity = yield getActivityDataByProductBaseInfo(origin); const C_VALUE = { type: '返YOHO币', des: '每件返 ', rest: '个 YOHO币' }; if (origin.productPriceBo.yohoCoinNum && origin.productPriceBo.yohoCoinNum !== 0) { result.activity.push({ type: C_VALUE.type, des: `${C_VALUE.des}${origin.productPriceBo.yohoCoinNum}${C_VALUE.rest}` }); } // 上市期 if (origin.expectArrivalTime) { result.arrivalDate = `${origin.expectArrivalTime}月`; result.presalePrice = origin.productPriceBo.formatSalesPrice; delete result.salePrice; result.hasOtherPrice = false; } // 商品咨询和评论数据,当前为空 let consultComment = getConsultCommentDataByProductInfo(origin); // 品牌信息 let banner = null; if (origin.brand) { result.brandImg = helpers.image(origin.brand.brandIco, 47, 47); result.brandName = origin.brand.brandName; result.brandUrl = helpers.urlFormat('', {}, origin.brand.brandDomain); banner = yield getBrandDataByProductBaseInfo(origin); if (banner.isCollect && favoriteData.brand) { banner.isCollect = favoriteData.brand; } } // sku商品信息 let skuData = getSkuDataByProductBaseInfo(origin); result.img = skuData.defaultImage; result.colors = skuData.skuGoods; let totalStorageNum = skuData.totalStorageNum; // 是否收藏 result.isCollect = favoriteData.product; if (origin.isLimitBuy === 'y') { // 是否开售 let isBeginSale = !!(origin.saleStatus && origin.saleStatus === 1); // 限购商品有关的展示状态 let showStatus = 1; if (origin.showStatus) { showStatus = parseInt(origin.showStatus); } let fashTopGoods = getFashionTopGoodsStatus(uid, showStatus, isBeginSale); result.fashTopGoods = { getLimitedCode: fashTopGoods.getLimitedCode, // 限购码状态 hasLimitedCode: fashTopGoods.hasLimitedCode, // 是否已经获取限购码 limitedCodeSoldOut: fashTopGoods.limitedCodeSoldOut, // 限购码是否已经抢光 getLimitedCodeDis: fashTopGoods.getLimitedCodeDis // 限购码是否失效 }; if (fashTopGoods.soldOut) { result.soldOut = fashTopGoods.soldOut; totalStorageNum = 0; // 改总数为已售磬 } else { result.openSoon = fashTopGoods.openSoon; // 即将开售 result.dis = fashTopGoods.dis; // 是否失效 result.buyNow = fashTopGoods.buyNow; // 是否立即购买 } } let soldOut = !!(origin.status === 0 || totalStorageNum === 0); let notForSale = origin.attribute === 2; // 非卖品 let virtualGoods = origin.attribute === 3; // 虚拟商品 if (!soldOut && !notForSale && !virtualGoods) { result.addToCart = true; // 立即购买或者即将开售存在 if (result.buyNow || result.openSoon) { delete result.addToCart; } } else if (notForSale) { // 非卖品 result.notForSale = true; } else if (soldOut) { // 已售磬 result.soldOut = true; delete result.fashTopGoods; } else if (virtualGoods) { // 虚拟商品 result.buyNow = true; // 是否立即购买 result.buyNowBase = helpers.urlFormat('/ticket', {}, 'shopping'); result.virtualGoods = virtualGoods; if (result.salePrice) { result.advancePrice = result.salePrice; // 先行价格 delete result.salePrice; } } // 去掉即将售罄 if (totalStorageNum || soldOut) { if (result.tags.isFew) { delete result.tags.isFew; // 去掉即将售罄 } } // 分享相关,产品的链接 result.weixinUrl = helpers.urlFormat(origin.productUrl, {}, 'item'); result.shareTitle = result.name; result.shareImg = result.img; result.shareDesc = result.phrase; // 统计需要的商品信息 let statGoodsInfo = {}; statGoodsInfo.uid = uid; statGoodsInfo.skn = origin.erpProductId; statGoodsInfo.productId = origin.id; statGoodsInfo.productName = result.name.replace('\'', '’'); statGoodsInfo.brandName = (result.brandName || '').replace('\'', '’'); statGoodsInfo.marketPrice = result.marketPrice.replace('¥', ''); if (result.salePrice) { statGoodsInfo.salePrice = result.salePrice.replace('¥', ''); } else { statGoodsInfo.salePrice = result.marketPrice.replace('¥', ''); } if (banner.brandId) { let domainBrand = yield BrandData.getBrandByDomainAsync(banner.brandDomain); if (domainBrand.type && domainBrand.shopId) { switch (parseInt(domainBrand.type)) { case 1: { // 多品店不显示 banner = []; break; } case 2: { // TODO:单品店显示新版的店铺banner,item.php 210 let basisData = ShopModel.basisTemplate(domainBrand.shopId); banner.bgImg = basisData.shopTopBanner.banner || banner.bgImg; break; } } } } statGoodsInfo.imageUrl = result.img; statGoodsInfo.productUrl = result.weixinUrl; statGoodsInfo.smallSortId = result.smallSortId; statGoodsInfo.soldOut = parseInt(soldOut); return { goodsInfo: result, consultComment: consultComment, banner: banner, statGoodsInfo: statGoodsInfo }; })();};
大家可以看到,使用 yield 之后,代码几乎就是跟php完全一样,我在写的过程中,也了解到了业务,同时完成了任务,就是这样。
3 结论
语言之争没有必要,重要的怎样解决问题的。通过使用 yield 使我较快的完成这项任务,节省了大量的时间,而我也终于知道了为什么 yield 如此的有用,为什么大家想方设法的去解决回调问题,因为在没有达到一定量级时,你确实没有感觉到回调的痛苦。多了回调确实很麻烦。
do not use co directly. use fn = co.wrap gen
& treat fn as async fn
. It will be easy to go to async/await
& better understanding
你的外部api 返回的是不是 promise对象啊?(我是小白)
//现阶段v8 引擎对 generator 的优化 简直可以和nvdia一拼var start,end;function test(){start=new Datevar a={}for (var i = 0; i < 10000000; i++) {a.value=i*(i-2)}end=new Date;console.log(end-start)}function test_quality(){start=new Datevar a_valuefor (var i = 0; i < 10000000; i++) {a_value=i*(i-2)}end=new Date;console.log(end-start)}
function g_test(){start=new Datevar a={}for (var i = 0; i < 10000000; i++) {a.value=i(i-2)}end=new Date;console.log(end-start)}function *g_test_quality(){start=new Date
for (let i = 0,a_value=0; i < 10000000; i++) {a_value=i*(i-2)}end=new Date;console.log(end-start)
}test();test_quality();(g_test()).next();(g_test_quality()).next()// 结果// 46// 16// 222// 1667//amazing// [Finished in 2.2s]
- 实际情况来看,还是yield很爽
- 大龄程序员的发展方向应该根据自己的实际情况选择做管理还是技术
- 好了还是没好呢?我来看看
- yield
- yield
- yield()
- yield
- yield
- yield
- yield
- yield
- yield
- yield
- yield
- yield
- yield
- 庚子赔款的最终实际情况
- Android单选按钮RadioButton来看你选男还是女
- 鸡兔同笼
- python java 查询指定文件路径下包含某些内容的文件,并输出文件路径
- Java多线程1:创建的三种方式以及比较
- Python中排序常用到的sort 、sorted和argsort函数
- RGB和YUV的转换与区别(视频编码)
- 实际情况来看,还是yield很爽
- ELK基础概念
- nvcc、gcc、g++混合编译器编程
- 现代控制系统-基于MATLAB和根轨迹的滞后超前校正
- 4-2 电子时钟中的运算符重载
- Java设计模式--命令模式
- Dubbo与Zookeeper的组合配置(1)
- 每日练习2017-11-08
- 5.1计算绝对值